From: Bill Erickson Date: Wed, 29 Oct 2014 21:06:51 +0000 (-0400) Subject: KMAIN-383 - Manage Authorities Interface - Search ID Enhancement X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=fb7babccc2919fc8abd351f6f7b7377026288717;p=working%2FEvergreen.git KMAIN-383 - Manage Authorities Interface - Search ID Enhancement Cross-port: 046a96d Conflicts: Open-ILS/src/perlmods/lib/OpenILS/Application/SuperCat.pm --- diff --git a/KCLS/openils/var/templates_kcls/cat/authority/list.tt2 b/KCLS/openils/var/templates_kcls/cat/authority/list.tt2 index 58237b6628..754a8473cf 100644 --- a/KCLS/openils/var/templates_kcls/cat/authority/list.tt2 +++ b/KCLS/openils/var/templates_kcls/cat/authority/list.tt2 @@ -34,7 +34,17 @@ + + [% l('Submit') %] + + + + + + [% l('Previous') %] - - - [% l('Submit') %] - - -
diff --git a/KCLS/openils/var/templates_kcls/cat/authority/list_id.tt2 b/KCLS/openils/var/templates_kcls/cat/authority/list_id.tt2 new file mode 100644 index 0000000000..855e001ec0 --- /dev/null +++ b/KCLS/openils/var/templates_kcls/cat/authority/list_id.tt2 @@ -0,0 +1,78 @@ +[% ctx.page_title = l('Authority record list') %] +[% WRAPPER base.tt2 %] + + + +
+ + + + + + + + + + + + [% l('Submit') %] + + + + + + + + + + [% l('Previous') %] + + + + [% l('Next') %] + + + +
+ +
+ + + + +
+ +[% END %] diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/authority.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/authority.pm index 7a27a7f46a..5e4b9c917f 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/authority.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/authority.pm @@ -304,7 +304,42 @@ sub authority_in_db_browse_or_search { /, {}, @args ); + return $list; +} +__PACKAGE__->register_method( + api_name => "open-ils.storage.authority.id_find", + method => "authority_id_find", + api_level => 1, + argc => 5, + signature => { + desc => q/Use stored procedures to perform authorities-based + browses or searches/, + params => [ + {name => "method", type => "string", desc => q/ + The name of a method within the authority schema to call. This + is an API call on a private service for a reason. Do not pass + unfiltered user input into this API call, especially in this + parameter./}, + {name => "what", type => "string", desc => q/ + What to search. Could be an axis name, an authority tag + number, or a bib tag number/}, + {name => "term", type => "string", desc => "Search term"}, + {name => "page", type => "number", desc => "Zero-based page number"}, + {name => "page_size", type => "number", + desc => "Number of records per page"} + ], + return => { + desc => "A list of authority record IDs", + type => "array" + } + } +); + +sub authority_id_find { + my ($self, $shift, $method, @args) = @_; + return unless $method =~ /^\w+$/; + my $list = ["@args[1]"]; return $list; } diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/SuperCat.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/SuperCat.pm index 2715571e19..40ffbbd0ad 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/SuperCat.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/SuperCat.pm @@ -303,17 +303,24 @@ sub generic_new_authorities_method { $term = naco_normalize($term); my $storage = create OpenSRF::AppSession("open-ils.storage"); - my $list = $storage->request( - "open-ils.storage.authority.in_db.browse_or_search", - $method, $what, $term, $page, $page_size, $thesauruses - )->gather(1); + my $list; + if ($what eq "id") { + $list = $storage->request( + "open-ils.storage.authority.id_find", + $method, $what, $term, $page, $page_size + )->gather(1); + } + else { + $list = $storage->request( + "open-ils.storage.authority.in_db.browse_or_search", + $method, $what, $term, $page, $page_size, $thesauruses + )->gather(1); + } $storage->kill_me; - return $list; } - sub tree_walker { my $tree = shift; my $field = shift; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/SuperCat.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/SuperCat.pm index b43d266069..94b27b2824 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/SuperCat.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/SuperCat.pm @@ -1697,9 +1697,11 @@ sub string_browse { my $next = join('/', $base,$format,$axis,$site,$string,$page + 1,$page_size,$thesauruses); unless ($string and $axis and grep { $axis eq $_ } keys %browse_types) { - warn "something's wrong..."; - warn " >>> format: $format -> axis: $axis -> site: $site -> string: $string -> page: $page -> page_size: $page_size "; - return undef; + unless ($axis eq "authority.id") { + warn "something's wrong..."; + warn " >>> format: $format -> axis: $axis -> site: $site -> string: $string -> page: $page -> page_size: $page_size "; + return undef; + } } $string = decode_utf8($string); @@ -1734,7 +1736,9 @@ sub string_browse { } (my $norm_format = $format) =~ s/(-full|-uris)$//o; - + if($axis eq 'authority.id') { + $axis = 'authority.title'; + }; my ($header,$content) = $browse_types{$axis}{$norm_format}->($tree,$prev,$next,$format,$unapi,$base,$site); print $header.$content; return Apache2::Const::OK; @@ -1822,7 +1826,6 @@ sub string_startwith { } (my $norm_format = $format) =~ s/(-full|-uris)$//o; - my ($header,$content) = $browse_types{$axis}{$norm_format}->($tree,$prev,$next,$format,$unapi,$base,$site); print $header.$content; return Apache2::Const::OK; diff --git a/Open-ILS/web/js/ui/kcls/cat/authority/list_id.js b/Open-ILS/web/js/ui/kcls/cat/authority/list_id.js new file mode 100644 index 0000000000..e28627f590 --- /dev/null +++ b/Open-ILS/web/js/ui/kcls/cat/authority/list_id.js @@ -0,0 +1,435 @@ +dojo.require('dijit.Dialog'); +dojo.require('dijit.form.Button'); +dojo.require('dijit.form.DropDownButton'); +dojo.require('dijit.form.FilteringSelect'); +dojo.require('dijit.form.Form'); +dojo.require('dijit.form.NumberSpinner'); +dojo.require('dijit.form.TextBox'); +dojo.require("dijit.Menu"); +dojo.require("dijit.MenuItem"); +dojo.require('dojox.xml.parser'); +dojo.require('DojoSRF'); +dojo.require("fieldmapper.Fieldmapper"); +dojo.require('openils.CGI'); +dojo.require('openils.PermaCrud'); +dojo.require('openils.XUL'); +dojo.require('openils.widget.OrgUnitFilteringSelect'); +dojo.require("openils.widget.PCrudAutocompleteBox"); +dojo.require("MARC.FixedFields"); +dojo.requireLocalization("openils.authority", "authority"); +var auth_strings = dojo.i18n.getLocalization("openils.authority", "authority"); + +var cgi = new openils.CGI(); +var pcrud = new openils.PermaCrud(); + +var _acs_cache_by_at = {}; +function fetch_control_set(thesaurus) { + if (!_acs_cache_by_at[thesaurus]) { + var at = pcrud.retrieve( + "at", thesaurus, + {"flesh": 1, "flesh_fields": {"at": ["control_set"]}} + ); + var cs; + if (at.control_set()) { + cs = at.control_set(); + } else { + cs = new fieldmapper.acs(); + cs.name("None"); // XXX i18n + + } + _acs_cache_by_at[thesaurus] = cs; + } + return _acs_cache_by_at[thesaurus]; +} + +/* +// OrgUnits do not currently affect the retrieval of authority records, +// but this is how to display them if they become OrgUnit-aware +function authOUListInit() { + new openils.User().buildPermOrgSelector( + "STAFF_LOGIN", // anywhere you can log in + dijit.byId("authOU"), + null, // pre-selected org + null + ); +} +dojo.addOnLoad(authOUListInit); +*/ +function displayAuthorities(data) { + var idArr = []; + var foundOne = 0; + + // Grab each record from the returned authority records + dojo.query("record", data).forEach(function(node) { + foundOne = 1; + var auth = {}; + auth.text = ''; + auth.thesaurus = '|'; + auth.id = 0; + auth.expand = ''; + + // Grab each authority record field from the authority record + dojo.query("datafield[tag^='1']", node).forEach(function(dfNode) { + auth.text += dojox.xml.parser.textContent(dfNode); + auth.name = dojo.attr(dfNode, 'tag'); + auth.ind1 = dojo.attr(dfNode, 'ind1'); + auth.ind2 = dojo.attr(dfNode, 'ind2'); + }); + + // Grab the ID of the authority record + dojo.query("datafield[tag='901']", node).query("subfield[code='c']").forEach(function(dfNode) { + auth.id = dojox.xml.parser.textContent(dfNode); + }); + + /* I wrap this in try/catch only because: + * a) this interface hasn't hitherto relied on MARC.Record, and + * b) the functionality we need it for is optional + */ + try { + var marc = new MARC.Record({"rtype": "AUT", "xml": node}); + auth.thesaurus = marc.extractFixedField("Subj", "|"); + } catch (E) { + console.warn( + "MARC.Record didn't work for authority record " + + auth.id + ": " + E + ); + } + + idArr.push(parseInt(auth.id)); + + // Create the authority record listing entry. XXX i18n + dojo.place( + '
' + + '
' + + '' + auth.text + '
ID: ' + + auth.id + '
' + + '
Control Set: ' + + fetch_control_set(auth.thesaurus).name() + + ' (#' + + fetch_control_set(auth.thesaurus).id() + ')
', + "authlist-div", "last" + ); + + // Add the menu of new/edit/delete/mark-for-merge options + var auth_menu = new dijit.Menu({}); + + // "Edit" menu item + new dijit.MenuItem({"id": "edit_" + auth.id, "onClick": function(){ + var auth_rec = pcrud.retrieve("are", auth.id); + if (auth_rec) { + loadMarcEditor(pcrud, auth_rec); + } + }, "label":auth_strings.MENU_EDIT}).placeAt(auth_menu, "first"); + + // "Merge" menu item + new dijit.MenuItem({"id": "merge_" + auth.id, "onClick":function(){ + auth.text = ''; + dojo.query('#auth' + auth.id).query('span.text').forEach(function(node) { + auth.text += dojox.xml.parser.textContent(node); + }); + + // If there is a toMerge item already, this is a target record + var mergeRole = ''; + var isTarget = dojo.query('.toMerge').length; + if (isTarget) { + mergeRole += auth_strings.TARGET_RECORD + ''; + } else { + mergeRole += auth_strings.MASTER_RECORD + ''; + } + + dojo.place('' + mergeRole + '' + auth.text + '', 'mergebox-tbody', 'last'); + dojo.place('' + auth.name + ' ' + auth.ind1 + auth.ind2 + '', 'mergeMeta_' + auth.id, 'last'); + dojo.removeClass('mergebox-div', 'hidden'); + }, "label":auth_strings.MENU_MERGE}).placeAt(auth_menu, "last"); + + // "Delete" menu item + new dijit.MenuItem({ + "id": "delete_" + auth.id, + "onClick":function(){ + auth.text = ''; + + var auth_rec = pcrud.retrieve("are", auth.id); + + // Bit of a hack to get the linked bib count until an explicit ID + var linkedBibs = dojox.xml.parser.textContent( + dojo.query("#authLabel" + auth.id)[0].previousSibling + ); + + var delDlg = dijit.byId("delDialog_" + auth.id); + + dojo.query('#auth' + auth.id).query('span.text').forEach(function(node) { + auth.text += dojo.trim(dojox.xml.parser.textContent(node)); + }); + + if (!delDlg) { + var content = '
' + dojo.string.substitute(auth_strings.CONFIRM_DELETE_TITLE, [auth.text]) + '
'; + if (parseInt(linkedBibs) > 0) { + content = "
" + + dojo.string.substitute(auth_strings.LINKED_BIBS, [linkedBibs]) + + "
"; + } + content += "
"; + content += ""; + content += ""; + content += ""; + content += ""; + content += "
"; + delDlg = new dijit.Dialog({ + "id":"delDialog_" + auth.id, + "title": dojo.string.substitute(auth_strings.CONFIRM_DELETE_PROMPT, [auth.id]), + "content": content + }); + } + delDlg.show(); + + }, "label":auth_strings.DELETE}).placeAt(auth_menu, "last"); + + auth_mb = new dijit.form.DropDownButton({dropDown: auth_menu, label: auth_strings.ACTIONS, id:"menu" + auth.id}); + auth_mb.placeAt(dojo.create("div", null, "auth" + auth.id, "first"), "first"); + auth_menu.startup(); + }); + + showBibCount(idArr); + if (foundOne == 0) { + window.alert("No Authority Record was found. \nPlease enter a valid Authority ID."); + } +} + +function viewMARC(recId) { + dojo.style(dojo.byId("authMARC" + recId), 'display', 'block'); + dojo.style(dijit.byId("viewMARC" + recId).domNode, 'display', 'none'); + dojo.style(dijit.byId("hideMARC" + recId).domNode, 'display', 'block'); +} + +function hideMARC(recId) { + dojo.style(dojo.byId("authMARC" + recId), 'display', 'none'); + dojo.style(dijit.byId("hideMARC" + recId).domNode, 'display', 'none'); + dojo.style(dijit.byId("viewMARC" + recId).domNode, 'display', 'block'); +} + +function marcToHTML(marc) { + var html = ''; + marc = dojox.xml.parser.parse(marc); + dojo.query('leader', marc).forEach(function(node) { + html += ''; + }); + dojo.query('controlfield', marc).forEach(function(node) { + html += ''; + }); + dojo.query('datafield', marc).forEach(function(node) { + var cnt = 0; + html += ''; + dojo.query('subfield', node).forEach(function(sf) { + if (cnt == 0) { + html += ''; + cnt = 1; + } else { + html += ''; + } + }); + }); + html += '
LDR  ' + dojox.xml.parser.textContent(node) + '
' + dojo.attr(node, "tag") + '  ' + dojox.xml.parser.textContent(node) + '
' + dojo.attr(node, "tag") + '' + dojo.attr(node, "ind1") + '' + dojo.attr(node, "ind2") + '$' + dojo.attr(sf, "code") + ' ' + dojox.xml.parser.textContent(sf) + '
$' + dojo.attr(sf, "code") + ' ' + dojox.xml.parser.textContent(sf) + '
'; + return html; +} + +function cancelDelete(recId) { + dijit.byId("delDialog_" + recId).hide(); +} + +function confirmDelete(recId) { + var auth_rec = pcrud.retrieve("are", recId); + if (auth_rec) { + pcrud.eliminate(auth_rec); + dijit.byId("delDialog_" + recId).attr("content", dojo.string.substitute(auth_strings.CONFIRM_DELETE_RESULT, [recId])); + setTimeout(function() { + dijit.byId("delDialog_" + recId).hide(); + }, 3000); + } +} + +function showBibCount(authIds) { + /* Decorate the list with # of bibs linked to each authority record */ + var ses = new OpenSRF.ClientSession('open-ils.cat'); + var req = ses.request('open-ils.cat.authority.records.count_linked_bibs', authIds); + var linkedIds = []; + req.oncomplete = function(r) { + var msg = r.recv().content(); + dojo.forEach(msg, function(auth) { + linkedIds.push(auth.authority); + dojo.place('' + auth.bibs + ' ', 'authLabel' + auth.authority, 'first'); + } + ); + + /* Assign counts of 0 for every non-linked authority */ + dojo.forEach(authIds, function (id) { + var found = false; + dojo.forEach(linkedIds, function (lid) { + if (id == lid) { + found = true; + } + }); + if (!found) { + dojo.place('0 ', 'authLabel' + id, 'first'); + } + }); + } + req.send(); +} + +function loadMarcEditor(pcrud, rec) { + + /* Prevent the spawned MARC editor from making its title bar inaccessible */ + var initHeight = self.outerHeight - 40; + /* Setting an explicit height results in a super skinny window, so fix that up */ + var initWidth = self.outerWidth / 2; + + /* + To run in Firefox directly, must set signed.applets.codebase_principal_support + to true in about:config + */ + win = window.open('/xul/server/cat/marcedit.xul','', // XXX version? + 'chrome,resizable=yes,height=' + initHeight + ',width=' + initWidth); + + win.xulG = { + "record": {"marc": rec.marc(), "rtype": "are"}, + "save": { + "label": auth_strings.SAVE, + "func": function(xmlString) { + rec.marc(xmlString); + rec.edit_date('now'); + rec.ischanged(true); + pcrud.update(rec); + alert(auth_strings.SAVE_RESULT_SUCCESS); + win.close(); + } + }, + 'lock_tab' : typeof xulG != 'undefined' ? (typeof xulG['lock_tab'] != 'undefined' ? xulG.lock_tab : undefined) : undefined, + 'unlock_tab' : typeof xulG != 'undefined' ? (typeof xulG['unlock_tab'] != 'undefined' ? xulG.unlock_tab : undefined) : undefined + }; +} + +function authListInit() { + var term = cgi.param('authTerm') || 0; + var page = cgi.param('authPage') || 0; + + if (page) { + dijit.byId('authPage').attr('value', page); + } + if (term) { + dijit.byId('authTerm').attr('value', term); + displayRecords(); + } + + dojo.connect(dijit.byId('authPage'), 'onKeyPress', function(evt) { + if (evt.keyCode == dojo.keys.ENTER) { + dijit.byId('authPage').attr('value', dijit.byId('authTerm').attr('value')); + displayRecords(); + } + }); + + dojo.connect(dijit.byId('authTerm'), 'onKeyPress', function(evt) { + if (evt.keyCode == dojo.keys.ENTER) { + dijit.byId('authPage').attr('value', dijit.byId('authTerm').attr('value')); + displayRecords(); + } + }); + + dijit.byId('authTerm').focus(); + +} +dojo.addOnLoad(authListInit); + +function updateNavFromTerm() { + dijit.byId('authPage').attr('value', dijit.byId('authTerm').attr('value')); +} + +function displayRecords(parms) { + + if (parms && parms.page) { + if (parms.page == 'next') { + page = dijit.byId('authPage').attr('value'); + dijit.byId('authPage').attr('value', page + 1); + } else if (parms.page == 'prev') { + page = dijit.byId('authPage').attr('value'); + dijit.byId('authPage').attr('value', page - 1); + } else { + dijit.byId('authPage').attr('value', parms.page); + } + } + + if (parms && parms.authId) { + if (parms.authId == 'next') { + term = dijit.byId('authTerm').attr('value'); + term++; + dijit.byId('authTerm').attr('value', term); + dijit.byId('authPage').attr('value', term); + } else if (parms.authId == 'prev') { + term = dijit.byId('authTerm').attr('value'); + term--; + dijit.byId('authTerm').attr('value', term); + dijit.byId('authPage').attr('value', term); + } else { + dijit.byId('authTerm').attr('value', parms.authId); + dijit.byId('authPage').attr('value', parms.authId); + } + } + + /* Protect against null input */ + if (!dijit.byId('authTerm').attr('value')) { + window.alert ("Please enter a valid Authority ID."); + return; + } + + /* Verify that Authority ID is a number */ + if(!dijit.byId('authTerm').attr('value').match(/^\d+$/)) { + window.alert ("Invalid Authority ID entered. \n Please enter a valid Authority ID."); + return; + } + + /* Clear out the current contents of the page */ + var widgets = dijit.findWidgets(dojo.byId('authlist-div')); + dojo.forEach(widgets, function(w) { w.destroyRecursive(true); }); + + dojo.query("#authlist-div").query("div").orphan(); + + var url = '/opac/extras/browse/marcxml/authority.id' + // + '/' + dijit.byId('authOU').attr('value') + + '/1' // replace with preceding line if OUs gain some meaning + + '/' + dijit.byId('authTerm').attr('value') + + '/' + dijit.byId('authPage').attr('value') + + '/' + '20' // 20 results per page + ; + dojo.xhrGet({"url":url, "handleAs":"xml", "content":{"format":"marcxml"}, "preventCache": true, "load":displayAuthorities }); +} + +function clearMergeRecords() { + var records = dojo.query('.toMerge').orphan(); + dojo.addClass('mergebox-div', 'hidden'); +} + +function mergeRecords() { + var records = dojo.query('.toMerge').attr('id'); + dojo.forEach(records, function(item, idx) { + records[idx] = parseInt(item.slice(item.lastIndexOf('_') + 1)); + }); + + /* Take the first record in the list and use that as the master */ + fieldmapper.standardRequest( + ['open-ils.cat', 'open-ils.cat.authority.records.merge'], + { async: false, + params: [openils.User.authtoken, records.shift(), records], + oncomplete : function(r) { + alert(auth_strings.MERGE_RESULT_SUCCESS); + clearMergeRecords(); + displayRecords(); + } + } + ); +} diff --git a/Open-ILS/web/opac/locale/en-US/lang.dtd b/Open-ILS/web/opac/locale/en-US/lang.dtd index b755ef9d2a..512b3d5b87 100644 --- a/Open-ILS/web/opac/locale/en-US/lang.dtd +++ b/Open-ILS/web/opac/locale/en-US/lang.dtd @@ -736,6 +736,7 @@ + @@ -968,6 +969,8 @@ + + diff --git a/Open-ILS/xul/staff_client/chrome/content/main/constants.js b/Open-ILS/xul/staff_client/chrome/content/main/constants.js index 5b465a4fb1..b7412821ae 100644 --- a/Open-ILS/xul/staff_client/chrome/content/main/constants.js +++ b/Open-ILS/xul/staff_client/chrome/content/main/constants.js @@ -400,6 +400,7 @@ var urls = { 'AUDIO_event_ASSET_COPY_NOT_FOUND' : '/xul/server/skin/media/audio/redalert.wav', 'AUTHORITY_MANAGE' : 'cat/authority/list', + 'FIND_AUTHORITY_BY_ID' : 'cat/authority/list_id', 'MANAGE_MULTI_HOME_ITEMS' : 'oils://remote/xul/server/cat/manage_multi_home_items.xul', 'XUL_AUTH_SIMPLE' : 'oils://remote/xul/server/main/simple_auth.xul', 'XUL_BIB_BRIEF' : 'oils://remote/xul/server/cat/bib_brief.xul', diff --git a/Open-ILS/xul/staff_client/chrome/content/main/menu.js b/Open-ILS/xul/staff_client/chrome/content/main/menu.js index 65af05763d..67c7ef6d49 100644 --- a/Open-ILS/xul/staff_client/chrome/content/main/menu.js +++ b/Open-ILS/xul/staff_client/chrome/content/main/menu.js @@ -1394,6 +1394,17 @@ main.menu.prototype = { } ], + 'cmd_find_authority_by_id' : [ + ['oncommand'], + function(event) { + open_eg_web_page( + urls.FIND_AUTHORITY_BY_ID, + "menu.cmd_find_authority_by_id.tab", + event + ); + } + ], + 'cmd_marc_batch_edit' : [ ['oncommand'], function(event) { diff --git a/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul b/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul index ca21c6c218..b333d0d404 100644 --- a/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul +++ b/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul @@ -26,6 +26,7 @@ + @@ -463,6 +464,7 @@ + diff --git a/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_overlay.xul b/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_overlay.xul index c96cefc4bd..3b0fed9a11 100644 --- a/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_overlay.xul +++ b/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_overlay.xul @@ -147,6 +147,11 @@ label="&staff.main.button_bar.authority_manage;" tooltiptext="&staff.main.button_bar.authority_manage;" /> +