Address LP #983487: Avoid clobbering bib records at authority merge
authorLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Tue, 5 Jun 2012 21:44:37 +0000 (17:44 -0400)
committerBill Erickson <berick@esilibrary.com>
Wed, 6 Jun 2012 13:44:03 +0000 (09:44 -0400)
If you've set up a relationship between bib record and authority records
based on arbitrary sets of controlling and controlled fields, but your
authority records don't have the right Subj fixed field value
corresponding to a control set that defines the controlling and
controlled fields you're using, authority merge operations may wipe out
lots of bib data.

Yamil Suarez encountered this bug in testing, and in his case what he
needed to do was set the Subj fixed field in his authority records to
'A' to match his Song Title Index (see the launchpad bug referened
above).

Previously, you could not actually save the Subj fixed field in the MARC
editor (which showed HeadSubj instead of Subj for authority records, and
didn't work).  Now you can.

Thanks to Mike Rylander for help in figuring out the above.

To provide additional protection against merging authority records when
they might not be linked with the right control set, the Manage
Authorities interface will now also show you the linked control set for
any given records.

The implementation of that last bit incidentally meant making sure flesh
and flesh_fields get passed through to PermaCrud for retrieve() calls via
the Javascript openils.PermaCrud wrapper.

Signed-off-by: Lebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Signed-off-by: Bill Erickson <berick@esilibrary.com>

Open-ILS/src/templates/cat/authority/list.tt2
Open-ILS/web/js/dojo/openils/PermaCrud.js
Open-ILS/web/js/ui/default/cat/authority/list.js
Open-ILS/xul/staff_client/server/cat/marcedit.xul

index c58040b..216a4cb 100644 (file)
@@ -2,7 +2,14 @@
 [% WRAPPER base.tt2 %]
 
 <script type="text/javascript" src='[% ctx.media_prefix %]/js/ui/default/cat/authority/list.js'> </script>
-
+<style type="text/css">
+    .authEntry { clear: both; }
+    .authEntry > *:first-child { float: left; width: 20%; }
+    .authEntry > .text { float: left; width: 46%; }
+    .authEntry > .authority-control-set { float: right; width: 33%; ; font-style: italic; }
+    .authority-control-set .acs-name { font-weight: bold; }
+    .authority-control-set .acs-id { }
+</style>
 <div dojoType="dijit.form.Form" id="myForm" jsId="myForm" encType="multipart/form-data" action="" method="">
     <span style="white-space: nowrap;">
         <label for="authTerm">Search term: </label>
index ca38357..c284c65 100644 (file)
@@ -106,10 +106,14 @@ if(!dojo._hasResource["openils.PermaCrud"]) {
 
         retrieve : function ( fm_class /* Fieldmapper class hint */, id /* Fieldmapper object primary key value */,  opts /* Option hash */) {
             if(!opts) opts = {};
+            var ffj = {};
+            if (opts.join) ffj.join = opts.join;
+            if (opts.flesh) ffj.flesh = opts.flesh;
+            if (opts.flesh_fields) ffj.flesh_fields = opts.flesh_fields;
             var req_hash = dojo.mixin(
                 opts, 
                 { method : 'open-ils.pcrud.retrieve.' + fm_class,
-                  params : [ this.auth(), id ]
+                  params : [ this.auth(), id, ffj ]
                 }
             );
 
index 591bfb8..7486427 100644 (file)
@@ -15,10 +15,24 @@ 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"]}}
+        );
+        _acs_cache_by_at[thesaurus] = at.control_set();
+    }
+    return _acs_cache_by_at[thesaurus];
+}
 
 /*
 // OrgUnits do not currently affect the retrieval of authority records,
@@ -40,6 +54,7 @@ function displayAuthorities(data) {
     dojo.query("record", data).forEach(function(node) {
         var auth = {};
         auth.text = '';
+        auth.thesaurus = '|';
         auth.id = 0;
 
         // Grab each authority record field from the authority record
@@ -56,17 +71,38 @@ function displayAuthorities(data) {
             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
-        dojo.place('<div class="authEntry" id="auth' + auth.id + '"><span class="text" id="authLabel' + auth.id + '">' + auth.text + '</span></div>', "authlist-div", "last");
+        // Create the authority record listing entry. XXX i18n
+        dojo.place(
+            '<div class="authEntry" id="auth' + auth.id + '">' +
+            '<div class="text" id="authLabel' + auth.id + '">' + auth.text + '</div>' +
+            '<div class="authority-control-set">Control Set: <span class="acs-name">' +
+            fetch_control_set(auth.thesaurus).name() +
+            '</span> <span class="acs-id">(#' +
+            fetch_control_set(auth.thesaurus).id() + ')</span></div></div>',
+            "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 pcrud = new openils.PermaCrud();
             var auth_rec = pcrud.retrieve("are", auth.id);
             if (auth_rec) {
                 loadMarcEditor(pcrud, auth_rec);
@@ -100,7 +136,6 @@ function displayAuthorities(data) {
             "onClick":function(){
                 auth.text = '';
 
-                var pcrud = new openils.PermaCrud();
                 var auth_rec = pcrud.retrieve("are", auth.id);
 
                 // Bit of a hack to get the linked bib count until an explicit ID
@@ -145,7 +180,7 @@ function displayAuthorities(data) {
         }, "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("auth" + auth.id, "first");
+        auth_mb.placeAt(dojo.create("div", null, "auth" + auth.id, "first"), "first");
         auth_menu.startup();
     });
 
@@ -194,7 +229,6 @@ function cancelDelete(recId) {
 }
 
 function confirmDelete(recId) {
-    var pcrud = new openils.PermaCrud();
     var auth_rec = pcrud.retrieve("are", recId);
     if (auth_rec) {
         pcrud.eliminate(auth_rec);
@@ -214,7 +248,7 @@ function showBibCount(authIds) {
         var msg = r.recv().content();
         dojo.forEach(msg, function(auth) {
                 linkedIds.push(auth.authority);
-                dojo.place('<span class="bibcount">' + auth.bibs + '</span>', 'authLabel' + auth.authority, 'before');
+                dojo.place('<span class="bibcount">' + auth.bibs + '</span> ', 'authLabel' + auth.authority, 'first');
             }
         );
 
@@ -227,7 +261,7 @@ function showBibCount(authIds) {
                 }
             });
             if (!found) {
-                dojo.place('<span class="bibcount">0</span>', 'authLabel' + id, 'before');
+                dojo.place('<span class="bibcount">0</span> ', 'authLabel' + id, 'first');
             }
         });
     }
index 7204883..195edae 100644 (file)
                             <textbox id="HeadMain_tb" context="clipboard" class="plain" name="HeadMain" maxlength="1" size="1" onkeypress="netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); if (!(event.altKey || event.ctrlKey || event.metaKey)) { oils_lock_page(); }" oninput="updateFixedFields(this);" onfocus="this.select();"/>
                         </row>
                         <row>
-                            <label name="HeadSubj" control="HeadSubj_tb" set="AUT" value="HeadSubj" />
-                            <textbox id="HeadSubj_tb" context="clipboard" class="plain" name="HeadSubj" maxlength="1" size="1" onkeypress="netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); if (!(event.altKey || event.ctrlKey || event.metaKey)) { oils_lock_page(); }" oninput="updateFixedFields(this);" onfocus="this.select();"/>
+                            <label name="Subj" control="Subj_tb" set="AUT" value="Subj" />
+                            <textbox id="Subj_tb" context="clipboard" class="plain" name="Subj" maxlength="1" size="1" onkeypress="netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); if (!(event.altKey || event.ctrlKey || event.metaKey)) { oils_lock_page(); }" oninput="updateFixedFields(this);" onfocus="this.select();"/>
                             <label name="HeadSer" control="HeadSer_tb" set="AUT" value="HeadSer" />
                             <textbox id="HeadSer_tb" context="clipboard" class="plain" name="HeadSer" maxlength="1" size="1" onkeypress="netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); if (!(event.altKey || event.ctrlKey || event.metaKey)) { oils_lock_page(); }" oninput="updateFixedFields(this);" onfocus="this.select();"/>
                             <label name="TypeSubd" control="TypeSubd_tb" set="AUT" value="TypeSubd" />