Fix LP949249, Edit, then Create button creates a new item in all cases
[transitory.git] / Open-ILS / xul / staff_client / server / cat / copy_editor.js
index 8680351..e430b8e 100644 (file)
@@ -1,17 +1,15 @@
-// vim:noet:sw=4:ts=4
-var g = {};
+// vim:et:sw=4:ts=4
+var g = { 'disabled' : false };
 g.map_acn = {};
 
-var xulG = {};
-
 function $(id) { return document.getElementById(id); }
+function $_(x) { return $('catStrings').getString(x); }
 
 function my_init() {
     try {
         /******************************************************************************************************/
         /* setup JSAN and some initial libraries */
 
-        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
         if (typeof JSAN == 'undefined') {
             throw( $('commonStrings').getString('common.jsan.missing') );
         }
@@ -24,13 +22,17 @@ function my_init() {
         JSAN.use('OpenILS.data'); g.data = new OpenILS.data(); g.data.init({'via':'stash'});
         JSAN.use('util.network'); g.network = new util.network();
 
-        g.docid = xul_param('docid',{'modal_xulG':true});
-        g.handle_update = xul_param('handle_update',{'modal_xulG':true});
+        if (xulG.unified_interface) {
+            $('non_unified_buttons').hidden = true;
+        }
+
+        g.docid = xul_param('docid');
+        g.handle_update = xul_param('handle_update');
 
         /******************************************************************************************************/
         /* Get the copy ids from various sources and flesh them */
 
-        var copy_ids = xul_param('copy_ids',{'concat':true,'JSON2js_if_cgi':true,'JSON2js_if_xulG':true,'JSON2js_if_xpcom':true,'stash_name':'temp_copy_ids','clear_xpcom':true,'modal_xulG':true});
+        var copy_ids = xul_param('copy_ids',{'concat':true,'JSON2js_if_cgi':true,'JSON2js_if_xulG':true,'JSON2js_if_xpcom':true,'stash_name':'temp_copy_ids','clear_xpcom':true});
         if (!copy_ids) copy_ids = [];
 
         if (copy_ids.length > 0) g.copies = g.network.simple_request(
@@ -42,14 +44,23 @@ function my_init() {
         /* And other fleshed copies if any */
 
         if (!g.copies) g.copies = [];
-        var c = xul_param('copies',{'concat':true,'JSON2js_if_cgi':true,'JSON2js_if_xpcom':true,'stash_name':'temp_copies','clear_xpcom':true,'modal_xulG':true})
+        var c = xul_param('copies',{'concat':true,'JSON2js_if_cgi':true,'JSON2js_if_xpcom':true,'stash_name':'temp_copies','clear_xpcom':true})
         if (c) g.copies = g.copies.concat(c);
 
         /******************************************************************************************************/
         /* We try to retrieve callnumbers for existing copies, but for new copies, we rely on this */
 
-        g.callnumbers = xul_param('callnumbers',{'concat':true,'JSON2js_if_cgi':true,'JSON2js_if_xpcom':true,'stash_name':'temp_callnumbers','clear_xpcom':true,'modal_xulG':true});
+        g.callnumbers = xul_param('callnumbers',{'concat':true,'JSON2js_if_cgi':true,'JSON2js_if_xpcom':true,'stash_name':'temp_callnumbers','clear_xpcom':true});
 
+        /******************************************************************************************************/
+        /* Get preference (if it exists) for copy location label order */
+
+        g.cl_first = false; // Default to legacy OU first mode
+        var prefs = Components.classes['@mozilla.org/preferences-service;1']
+            .getService(Components.interfaces['nsIPrefBranch']);
+        try {
+            g.cl_first = prefs.getBoolPref('oils.copy_editor.copy_location_name_first');
+        } catch(E) { }
 
         /******************************************************************************************************/
         /* Quick fix, this was defined inline in the global scope but now needs g.error and g.copies from my_init */
@@ -61,64 +72,66 @@ function my_init() {
         /******************************************************************************************************/
         /* Is the interface an editor or a viewer, single or multi copy, existing copies or new copies? */
 
-        if (xul_param('edit',{'modal_xulG':true}) == '1') { 
+        if (xul_param('edit') == '1') { 
 
-            // Editor desired, but let's check permissions
             g.edit = false;
 
-            try {
-                var check = g.network.simple_request(
-                    'PERM_MULTI_ORG_CHECK',
-                    [ 
-                        ses(), 
-                        g.data.list.au[0].id(), 
-                        util.functional.map_list(
-                            g.copies,
-                            function (o) {
-                                var lib;
-                                var cn_id = o.call_number();
-                                if (cn_id == -1) {
-                                    lib = o.circ_lib(); // base perms on circ_lib instead of owning_lib if pre-cat
-                                } else {
-                                    if (! g.map_acn[ cn_id ]) {
-                                        var req = g.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ cn_id ]);
-                                        if (typeof req.ilsevent == 'undefined') {
-                                            g.map_acn[ cn_id ] = req;
-                                            lib = g.map_acn[ cn_id ].owning_lib();
+            if (g.copies.length > 0) { // When loaded in the unified interface, there may be no copies yet (from the volum/item creator) 
+
+                // Editor desired, but let's check permissions
+
+                try {
+                    var check = g.network.simple_request(
+                        'PERM_MULTI_ORG_CHECK',
+                        [ 
+                            ses(), 
+                            g.data.list.au[0].id(), 
+                            util.functional.map_list(
+                                g.copies,
+                                function (o) {
+                                    var lib;
+                                    var cn_id = o.call_number();
+                                    if (cn_id == -1) {
+                                        lib = o.circ_lib(); // base perms on circ_lib instead of owning_lib if pre-cat
+                                    } else {
+                                        if (! g.map_acn[ cn_id ]) {
+                                            var req = g.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ cn_id ]);
+                                            if (typeof req.ilsevent == 'undefined') {
+                                                g.map_acn[ cn_id ] = req;
+                                                lib = g.map_acn[ cn_id ].owning_lib();
+                                            } else {
+                                                lib = o.circ_lib();
+                                            }
                                         } else {
-                                            lib = o.circ_lib();
+                                            lib = g.map_acn[ cn_id ].owning_lib();
                                         }
-                                    } else {
-                                        lib = g.map_acn[ cn_id ].owning_lib();
                                     }
+                                    return typeof lib == 'object' ? lib.id() : lib;
                                 }
-                                return typeof lib == 'object' ? lib.id() : lib;
-                            }
-                        ),
-                        g.copies.length == 1 ? [ 'UPDATE_COPY' ] : [ 'UPDATE_COPY', 'UPDATE_BATCH_COPY' ]
-                    ]
-                );
-                g.edit = check.length == 0;
-            } catch(E) {
-                g.error.standard_unexpected_error_alert('batch permission check',E);
+                            ),
+                            g.copies.length == 1 ? [ 'UPDATE_COPY' ] : [ 'UPDATE_COPY', 'UPDATE_BATCH_COPY' ]
+                        ]
+                    );
+                    g.edit = check.length == 0;
+                } catch(E) {
+                    g.error.standard_unexpected_error_alert('batch permission check',E);
+                }
+
             }
 
             if (g.edit) {
                 $('caption').setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.caption')); 
                 $('save').setAttribute('hidden','false'); 
-                g.retrieve_templates();
             } else {
                 $('top_nav').setAttribute('hidden','true');
             }
+
+            g.retrieve_templates();
+
         } else {
             $('top_nav').setAttribute('hidden','true');
         }
 
-        if (g.copies.length > 0 && g.copies[0].id() < 0) {
-            document.getElementById('copy_notes').setAttribute('hidden','true');
-            $('save').setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.create_copies'));
-            $('save').setAttribute('accesskey', $('catStrings').getString('staff.cat.copy_editor.create_copies.accesskey'));
-        }
         g.panes_and_field_names.left_pane = 
             [
                 [
@@ -131,9 +144,6 @@ function my_init() {
                 ]
             ].concat(g.panes_and_field_names.left_pane);
 
-        if (g.copies.length != 1) {
-            document.getElementById('copy_notes').setAttribute('hidden','true');
-        }
 
         /******************************************************************************************************/
         /* Show the Record Details? */
@@ -162,6 +172,61 @@ function my_init() {
         g.render();
         g.check_for_unmet_required_fields();
 
+        if (xulG.unified_interface) {
+            xulG.disable_copy_editor = function(c) {
+                addCSSClass(document.documentElement,'disabled_copy_editor');
+                g.disabled = true;
+            }
+            xulG.enable_copy_editor = function(c) {
+                removeCSSClass(document.documentElement,'disabled_copy_editor');
+                g.disabled = false;
+                xulG.refresh_copy_editor();
+            }
+            xulG.refresh_copy_editor = function() {
+                dump('refresh_copy_editor\n');
+                addCSSClass(document.documentElement,'enabling_copy_editor');
+                try {
+                    xulG.clear_update_copy_editor_timeout();
+                    g.copies = xulG.copies;
+                    g.edit = g.copies.length > 0;
+                    if (g.edit) {
+                        $('caption').setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.caption'));
+                    }
+                    g.original_copies = js2JSON( g.copies );
+                    g.hide_copy_notes_button();
+                    for (var i = 0; i < g.applied_templates.length; i++) {
+                        g._apply_template( g.applied_templates[i], false);
+                    }
+                    if (g.copies.length > 0) {
+                        // Stop tracking these templates once they're applied
+                        // to actual copies
+                        g.applied_templates = [];
+                    }
+                    g.summarize( g.copies );
+                    g.render();
+                    g.check_for_unmet_required_fields();
+                    setTimeout(
+                        function() {
+                            removeCSSClass(document.documentElement,'enabling_copy_editor');
+                        }, 1000
+                    );
+                } catch(E) {
+                    alert('Error in copy_editor.js, xulG.refresh_copy_editor(): ' + E);
+                }
+            };
+            xulG.unlock_copy_editor = function() {
+                oils_unlock_page();
+            };
+            xulG.notify_of_templatable_field_change = function(id,v) {
+                g.changed[ 'volume_copy_creator.'+id ] = { 'type' : 'volume_copy_creator', 'field' : id, 'value' : v };
+            }
+        } else {
+            g.hide_copy_notes_button();
+        }
+
+        JSAN.use('util.hide');
+        util.hide.generate_css('ui.hide_copy_editor_fields');
+
     } catch(E) {
         var err_msg = $("commonStrings").getFormattedString('common.exception', ['cat/copy_editor.js', E]);
         try { g.error.sdump('D_ERROR',err_msg); } catch(E) { dump(err_msg); dump(js2JSON(E)); }
@@ -170,6 +235,19 @@ function my_init() {
 }
 
 /******************************************************************************************************/
+/* Show copy notes button */
+g.hide_copy_notes_button = function() {
+    if (g.copies.length > 0 && g.copies[0].id() < 0) {
+        document.getElementById('copy_notes').setAttribute('hidden','true');
+        $('save').setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.create_copies'));
+        $('save').setAttribute('accesskey', $('catStrings').getString('staff.cat.copy_editor.create_copies.accesskey'));
+    }
+    if (g.copies.length != 1) {
+        document.getElementById('copy_notes').setAttribute('hidden','true');
+    }
+}
+
+/******************************************************************************************************/
 /* Retrieve Templates */
 
 g.retrieve_templates = function() {
@@ -191,6 +269,26 @@ g.retrieve_templates = function() {
             function() { g.copy_editor_prefs[ 'template_menu' ] = { 'value' : g.template_menu.value }; g.save_attributes(); },
             false
         );
+
+        if (xulG.unified_interface) {
+            if (typeof xulG.update_unified_template_list == 'function') {
+                xulG.update_unified_template_list(list);
+                // functions the unified wrapper should use to let the item attribute editor do the heavy lifting for templates
+                xulG.update_item_editor_template_selection = function(new_value) {
+                    g.template_menu.setAttribute('value', new_value);
+                    g.template_menu.value = new_value;
+                    g.copy_editor_prefs[ 'template_menu' ] = { 'value' : g.template_menu.value };
+                    g.save_attributes();
+                }
+                xulG.item_editor_apply_template = function() { g.apply_template(true); };
+                xulG.item_editor_delete_template = function() { g.delete_template(); };
+                xulG.item_editor_save_template = function() { g.save_template(); };
+                xulG.item_editor_import_templates = function() { g.import_templates(); };
+                xulG.item_editor_export_templates = function() { g.export_templates(); };
+                xulG.item_editor_reset = function() { g.reset(); };
+            }
+        }
+
     } catch(E) {
         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.retrieve_templates.error'), E);
     }
@@ -199,12 +297,46 @@ g.retrieve_templates = function() {
 /******************************************************************************************************/
 /* Apply Template */
 
-g.apply_template = function() {
+g.applied_templates = [];
+
+g.apply_template = function(apply_volume_editor_template_changes) {
     try {
         var name = g.template_menu.value;
         if (g.templates[ name ] != 'undefined') {
+            if (g.copies == 0) {
+                // We're only tracking these applied templates temporarily,
+                // specifically when they're used prior to copies being
+                // created in the unified interface.
+                g.applied_templates.push( name );
+            }
+            g._apply_template(name,apply_volume_editor_template_changes);
+            g.summarize( g.copies );
+            g.render();
+            g.check_for_unmet_required_fields();
+        }
+    } catch(E) {
+        g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.apply_templates.error'), E);
+    }
+}
+
+g._apply_template = function(name,apply_volume_editor_template_changes) {
+    try {
+        if (g.templates[ name ] != 'undefined') {
             var template = g.templates[ name ];
             for (var i in template) {
+                if (g.is_field_hidden(i)) {
+                    alert($('catStrings').getFormattedString(
+                        'staff.cat.copy_editor.apply_unsafe_field',
+                        [i]
+                    ));
+                    continue;
+                }
+                if (template[i].field == 'status') {
+                    if (!g.safe_to_edit_copy_status()) {
+                        alert($('catStrings').getFormattedString('staff.cat.copy_editor.apply_unsafe_field',[i]));
+                        continue;
+                    }
+                }
                 g.changed[ i ] = template[ i ];
                 switch( template[i].type ) {
                     case 'attribute' :
@@ -216,14 +348,16 @@ g.apply_template = function() {
                     case 'owning_lib' :
                         g.apply_owning_lib(template[i].value);
                     break;
+                    case 'volume_copy_creator' :
+                        if (xulG.unified_interface && apply_volume_editor_template_changes) {
+                            xulG.apply_template_to_batch(template[i].field,template[i].value);
+                        }
+                    break;
                 }
             }
-            g.summarize( g.copies );
-            g.render();
-            g.check_for_unmet_required_fields();
         }
     } catch(E) {
-        g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.apply_templates.error'), E);
+        alert('Error in copy_editor.js, g._apply_template('+name+'): ' + E);
     }
 }
 
@@ -297,7 +431,6 @@ g.delete_template = function() {
 
 g.export_templates = function() {
     try {
-        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
         JSAN.use('util.file'); var f = new util.file('');
         f.export_file( { 'title' : $('catStrings').getString('staff.cat.copy_editor.export_templates.title'), 'data' : g.templates } );
     } catch(E) {
@@ -310,7 +443,6 @@ g.export_templates = function() {
 
 g.import_templates = function() {
     try {
-        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
         JSAN.use('util.file'); var f = new util.file('');
         var temp = f.import_file( { 'title' : $('catStrings').getString('staff.cat.copy_editor.import_templates.title') } );
         if (temp) {
@@ -383,12 +515,16 @@ g.import_templates = function() {
 /* Restore backup copies */
 
 g.reset = function() {
+    g.applied_templates = [];
     g.changed = {};
     g.copies = JSON2js( g.original_copies );
     g.summarize( g.copies );
     g.render();
     g.check_for_unmet_required_fields();
     oils_unlock_page();
+    if (xulG.unified_interface) {
+        xulG.reset_batch_menus();
+    }
 }
 
 /******************************************************************************************************/
@@ -459,6 +595,8 @@ g.apply_stat_cat = function(sc_id,entry_id) {
 
 g.apply_owning_lib = function(ou_id) {
     g.error.sdump('D_TRACE','ou_id = ' + ou_id + '\n');
+    // but don't allow this when bundled with the volume/copy creator UI, or if we're editing pre-cats
+    if (! g.safe_to_change_owning_lib() ) { return; }
     for (var i = 0; i < g.copies.length; i++) {
         var copy = g.copies[i];
         try {
@@ -471,15 +609,15 @@ g.apply_owning_lib = function(ou_id) {
                 g.map_acn[copy.call_number()] = volume;
             }
             var old_volume = g.map_acn[copy.call_number()];
-            var acn_id = g.network.simple_request(
+            var acn_blob = g.network.simple_request(
                 'FM_ACN_FIND_OR_CREATE',
-                [ses(),old_volume.label(),old_volume.record(),ou_id]
+                [ses(),old_volume.label(),old_volume.record(),ou_id,old_volume.prefix().id(),old_volume.suffix().id(),old_volume.label_class().id()]
             );
-            if (typeof acn_id.ilsevent != 'undefined') {
-                g.error.standard_unexpected_error_alert($('catStrings').getFormattedString('staff.cat.copy_editor.apply_owning_lib.call_number.error', [copy.barcode()]), acn_id);
+            if (typeof acn_blob.ilsevent != 'undefined') {
+                g.error.standard_unexpected_error_alert($('catStrings').getFormattedString('staff.cat.copy_editor.apply_owning_lib.call_number.error', [copy.barcode()]), acn_blob);
                 continue;
             }
-            copy.call_number(acn_id);
+            copy.call_number(acn_blob.acn_id);
             copy.ischanged('1');
         } catch(E) {
             g.error.standard_unexpected_error_alert('apply_stat_cat',E);
@@ -490,10 +628,11 @@ g.apply_owning_lib = function(ou_id) {
 }
 
 /******************************************************************************************************/
-/* This returns true if none of the copies being edited are pre-cats */
+/* This returns false if any of the copies being edited are pre-cats, or if we're embedded in the unified volume/copy UI */
 
 g.safe_to_change_owning_lib = function() {
     try {
+        if (xulG.unified_interface) { return false; }
         var safe = true;
         for (var i = 0; i < g.copies.length; i++) {
             var cn = g.copies[i].call_number();
@@ -525,6 +664,23 @@ g.safe_to_edit_copy_status = function() {
 }
 
 /******************************************************************************************************/
+/* This returns true if the field has been hidden via util.hide */
+
+g.is_field_hidden = function(field) {
+    try {
+        g.data.stash_retrieve();
+        if (g.data.hash.aous['ui.hide_copy_editor_fields']
+            && g.data.hash.aous['ui.hide_copy_editor_fields'].indexOf(field) > -1) {
+            return true;
+        }
+    } catch(E) {
+        g.error.standard_unexpected_error_alert('is_field_hidden?',E);
+        return false;
+    }
+}
+
+
+/******************************************************************************************************/
 /* This concats and uniques all the alert messages for use as the default value for a new alert message */
 
 g.populate_alert_message_input = function(tb) {
@@ -773,6 +929,13 @@ g.panes_and_field_names = {
         $('catStrings').getString('staff.cat.copy_editor.field.barcode.label'),
         {
             render: 'fm.barcode();',
+            input:
+                  'c = function (v) {'
+                +     'g.apply("barcode", v);'
+                +     'if (typeof post_c === "function") post_c(v);'
+                + '};'
+                + 'x = document.createElement("textbox");',
+            attr: { 'class': 'disabled' },
         }
     ], 
     [
@@ -782,6 +945,12 @@ g.panes_and_field_names = {
         }
     ],
     [
+        $('catStrings').getString('staff.cat.copy_editor.field.active_date.label'),
+        { 
+            render: 'util.date.formatted_date( fm.active_date(), "%F");',
+        }
+    ],
+    [
         $('catStrings').getString('staff.cat.copy_editor.field.creator.label'),
         { 
             render: 'fm.creator();',
@@ -808,7 +977,7 @@ g.panes_and_field_names = {
         $('catStrings').getString('staff.cat.copy_editor.field.location.label'),
         { 
             render: 'typeof fm.location() == "object" ? fm.location().name() : g.data.lookup("acpl",fm.location()).name()', 
-            input: 'c = function(v){ g.apply("location",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( g.get_acpl_list(), function(obj) { return [ g.data.hash.aou[ obj.owning_lib() ].shortname() + " : " + obj.name(), obj.id() ]; }).sort()); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
+            input: 'c = function(v){ g.apply("location",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( g.get_acpl_list(), function(obj) { return [ ' + (g.cl_first ? 'obj.name() + " : " + g.data.hash.aou[ obj.owning_lib() ].shortname()' : 'g.data.hash.aou[ obj.owning_lib() ].shortname() + " : " + obj.name()') + ', obj.id() ]; }).sort()); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
 
         }
     ],
@@ -1078,6 +1247,7 @@ g.render = function() {
             try {
                 var f = g.panes_and_field_names[h][i]; var fn = f[0]; var attr = f[1].attr;
                 groupbox = document.createElement('groupbox'); document.getElementById(h).appendChild(groupbox);
+                groupbox.setAttribute('hideable',fn);
                 if (attr) {
                     for (var a in attr) {
                         groupbox.setAttribute(a,attr[a]);
@@ -1086,6 +1256,7 @@ g.render = function() {
                 caption = document.createElement('caption'); groupbox.appendChild(caption);
                 caption.setAttribute('label',fn);
                 caption.setAttribute('id','caption_'+fn); // used for focus/keyboard navigation
+                caption.setAttribute('hideable',fn);
                 vbox = document.createElement('vbox'); groupbox.appendChild(vbox); // main display widget goes here
                 if (typeof g.changed[fn] != 'undefined') { addCSSClass(vbox,'copy_editor_field_changed'); }
                 if (typeof g.required[fn] != 'undefined') { addCSSClass(vbox,'copy_editor_field_required'); }
@@ -1148,8 +1319,16 @@ g.render = function() {
             } catch(E) { alert(E); }
         }
     }
-    if (g.template_menu) g.template_menu.value = g.template_menu.getAttribute('value');
+    if (g.template_menu) {
+        g.template_menu.value = g.template_menu.getAttribute('value');
+        if (xulG.unified_interface) {
+            if (typeof xulG.update_unified_template_selection == 'function') {
+                xulG.update_unified_template_selection(g.template_menu.value);
+            }
+        }
+    }
 
+    util.hide.generate_css('ui.hide_copy_editor_fields');
 }
 
 /******************************************************************************************************/
@@ -1178,16 +1357,15 @@ g.render_input = function(node,blob) {
             groupbox.setAttribute('style','');
         }
 
-        vbox.addEventListener('mouseover',on_mouseover,false);
-        vbox.addEventListener('mouseout',on_mouseout,false);
         groupbox.addEventListener('mouseover',on_mouseover,false);
         groupbox.addEventListener('mouseout',on_mouseout,false);
-        groupbox.firstChild.addEventListener('mouseover',on_mouseover,false);
-        groupbox.firstChild.addEventListener('mouseout',on_mouseout,false);
 
         function on_click(ev){
             try {
-                if (block) return; block = true;
+                if (block || g.disabled || !g.edit || ev.currentTarget.classList.contains('disabled')) {
+                    return;
+                }
+                block = true;
 
                 oils_lock_page();
 
@@ -1251,10 +1429,8 @@ g.render_input = function(node,blob) {
                 g.error.standard_unexpected_error_alert('render_input',E);
             }
         }
-        vbox.addEventListener('click',on_click, false);
-        hbox.addEventListener('click',on_click, false);
-        caption.addEventListener('click',on_click, false);
-        caption.addEventListener('keypress',function(ev) {
+        groupbox.addEventListener('click',on_click, false);
+        groupbox.addEventListener('keypress',function(ev) {
             if (ev.keyCode == 13 /* enter */ || ev.keyCode == 77 /* mac enter */) on_click();
         }, false);
         caption.setAttribute('style','-moz-user-focus: normal');
@@ -1270,17 +1446,24 @@ g.render_input = function(node,blob) {
 /* store the copies in the global xpcom stash */
 
 g.stash_and_close = function() {
+    var r = {textcode: ''};
     try {
         oils_unlock_page();
 
         if (g.handle_update) {
             try {
-                var r = g.network.request(
+                r = g.network.request(
                     api.FM_ACP_FLESHED_BATCH_UPDATE.app,
                     api.FM_ACP_FLESHED_BATCH_UPDATE.method,
                     [ ses(), g.copies, true ]
                 );
-                if (typeof r.ilsevent != 'undefined') {
+                if (r.textcode === 'ITEM_BARCODE_EXISTS') {
+                    alert('error with item update: ' + r.desc);
+                    var barcode = $($_('staff.cat.copy_editor.field.barcode.label'));
+                    barcode.parentNode.classList.remove('disabled');
+                    barcode.click();
+                }
+                else if (typeof r.ilsevent !== 'undefined') {
                     g.error.standard_unexpected_error_alert('copy update',r);
                 }
                 /* FIXME -- revisit the return value here */
@@ -1291,9 +1474,10 @@ g.stash_and_close = function() {
         //g.data.temp_copies = js2JSON( g.copies );
         //g.data.stash('temp_copies');
         xulG.copies = g.copies;
-        update_modal_xulG(xulG);
-        JSAN.use('util.widgets');
-        util.widgets.dispatch('close',window);
+        if (r.textcode !== 'ITEM_BARCODE_EXISTS') {
+            JSAN.use('util.widgets');
+            util.widgets.dispatch('close',window);
+        }
     } catch(E) {
         alert('Error in copy_editor.js, g.stash_and_close(): '+E);
     }
@@ -1503,10 +1687,22 @@ g.check_for_unmet_required_fields = function() {
             abort.push(fn);
         }
     }
-    if (abort.length > 0) {
-        $('save').setAttribute('disabled','true'); 
+    if (xulG.unified_interface) {
+        if (abort.length > 0) {
+            if (typeof xulG.lock_save_button == 'function') {
+                xulG.lock_save_button();
+            }
+        } else {
+            if (typeof xulG.unlock_save_button == 'function') {
+                xulG.unlock_save_button();
+            }
+        }
     } else {
-        $('save').setAttribute('disabled','false'); 
+        if (abort.length > 0) {
+            $('save').setAttribute('disabled','true');
+        } else {
+            $('save').setAttribute('disabled','false');
+        }
     }
 }