LP#1099979 Provide facility to merge parts in the Monograph Parts display
authorDan Pearl <dpearl@cwmars.org>
Mon, 4 Nov 2013 19:18:20 +0000 (14:18 -0500)
committerBen Shum <bshum@biblio.org>
Mon, 28 Jul 2014 18:19:13 +0000 (14:19 -0400)
A new facility is provided to make it relatively simple to merge parts that should be named the same, but are
not.  The user will check-off the parts that need to be merged, and click on the new Merge Selected Parts
control, at which point the "winner" will be selected from a pop-up.

Signed-off-by: Dan Pearl <dpearl@cwmars.org>
Signed-off-by: Rogan Hamby <rogan.hamby@gmail.com>
Signed-off-by: Ben Shum <bshum@biblio.org>

Open-ILS/src/templates/conify/global/biblio/monograph_part.tt2
Open-ILS/web/css/skin/default.css
Open-ILS/web/css/skin/default/biblio.css [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/biblio/monographPartMerge.js [new file with mode: 0755]
Open-ILS/web/js/dojo/openils/biblio/nls/biblio_messages.js [new file with mode: 0755]
docs/RELEASE_NOTES_NEXT/Cataloging/merge-parts.txt [new file with mode: 0644]

index b504200..dfc89cc 100644 (file)
@@ -1,37 +1,43 @@
 [% WRAPPER base.tt2 %]
 [% ctx.page_title = l('Configure Monograph Parts') %]
-<div dojoType="dijit.layout.ContentPane" layoutAlign="client">
-    <div dojoType="dijit.layout.ContentPane" layoutAlign="top" class='oils-header-panel'>
-        <div>[% l('Monograph Parts') %]</div>
-        <div>
-            <button dojoType='dijit.form.Button' onClick='monoPartGrid.showCreateDialog()'>[% l('New Monograph Part') %]</button>
-            <button dojoType='dijit.form.Button' onClick='monoPartGrid.deleteSelected()'>[% l('Delete Selected') %]</button>
+    <div dojoType="dijit.layout.BorderContainer" design="headline" >
+
+        <div dojoType="dijit.layout.ContentPane" layoutAlign="top" class='oils-header-panel' region="top" style="height:30px;">
+            <div>[% l('Monograph Parts') %]</div>
+            <div>
+               <button dojoType='dijit.form.Button' onClick='openils.biblio.monographPartMerge.showMergeDialog(monoPartGrid)'>[% l('Merge Selected') %]</button>
+               <button dojoType='dijit.form.Button' onClick='monoPartGrid.showCreateDialog()'>[% l('New Monograph Part') %]</button>
+               <button dojoType='dijit.form.Button' onClick='monoPartGrid.deleteSelected()'>[% l('Delete Selected') %]</button>
+               &nbsp;&nbsp;
+            </div>
         </div>
-    </div>
-    <div>
-    <table  jsId="monoPartGrid"
+     <div id="sortContentPane" dojoType="dijit.layout.ContentPane" layoutAlign="client" region="center" style="width:500px;overflow-x:hidden;">
+       <table id="monoGrid" jsId="monoPartGrid"
             dojoType="openils.widget.AutoGrid"
-            autoHeight='true'
+            hidePaginator='true'
+            autoHeight='true' 
             fieldOrder="['label']"
+            displayLimit=0 
             suppressFields="['id','record','label_sortkey']"
             suppressEditFields="['id','label_sortkey']"
             query="{id: null}"
             fmClass='bmp'
             editOnEnter='true'/>
-</div>
+     </div>
+  </div>
 
 <script type="text/javascript">
+    dojo.require('dijit.layout.BorderContainer');
     dojo.require('openils.CGI');
     dojo.require('openils.Util');
     dojo.require('openils.widget.AutoGrid');
+    dojo.require('openils.biblio.monographPartMerge');
 
     var cgi = new openils.CGI();
     openils.Util.addOnLoad( function() {
         monoPartGrid.overrideEditWidgets.record = new dijit.form.TextBox({"disabled": true});
         monoPartGrid.overrideEditWidgets.record.shove = { create : cgi.param('r') };
-        monoPartGrid.loadAll({order_by : {bmp : 'label'}}, {record : cgi.param('r')});
+        monoPartGrid.loadAll({order_by : [{class : 'bmp', field : 'label_sortkey'}]}, {record : cgi.param('r')});
     });
 </script>
 [% END %]
-
-
index af96f07..63bed99 100644 (file)
@@ -2,6 +2,7 @@
 @import "default/acq.css";
 @import "default/admin.css";
 @import "default/serial.css";
+@import "default/biblio.css";
 /* import the dojo CSS */
 @import "/js/dojo/dojo/resources/dojo.css";
 @import "/js/dojo/dijit/themes/tundra/tundra.css";
diff --git a/Open-ILS/web/css/skin/default/biblio.css b/Open-ILS/web/css/skin/default/biblio.css
new file mode 100644 (file)
index 0000000..7da8f15
--- /dev/null
@@ -0,0 +1,12 @@
+.biblio-merge-item {
+       font-family : monospace;
+        font-weight : bold;
+        font-size   : 16px;
+}
+
+.biblio-merge-item:hover {
+        background-color : LawnGreen;
+}
+.biblio-merge-prevail-title {
+        font-weight : bold;
+}
diff --git a/Open-ILS/web/js/dojo/openils/biblio/monographPartMerge.js b/Open-ILS/web/js/dojo/openils/biblio/monographPartMerge.js
new file mode 100755 (executable)
index 0000000..59b557e
--- /dev/null
@@ -0,0 +1,142 @@
+/* ---------------------------------------------------------------------------
+ * Copyright (C) 2014  C/W MARS Inc.
+ * Dan Pearl <dpearl@cwmars.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * ---------------------------------------------------------------------------
+ */
+
+/*
+ * Support for the facility to merge multiple part designations into
+ * one.
+ */
+
+if(!dojo._hasResource["openils.biblio.monographPartMerge"]) {
+    dojo._hasResource["openils.biblio.monographPartMerge"] = true;
+    dojo.provide("openils.biblio.monographPartMerge");
+    dojo.declare("openils.biblio.monographPartMerge", null, {}); 
+
+    /*
+     * Generate a pop-up to control part merging 
+     */
+    openils.biblio.monographPartMerge.showMergeDialog = function(gridControl) {
+         dojo.requireLocalization("openils.biblio","biblio_messages");
+
+       var items = gridControl.getSelectedItems();
+       var total = items.length;
+
+       if (total < 2 ) // Validate number of selected items
+           {
+           alert(dojo.i18n.getLocalization("openils.biblio","biblio_messages").SELECT_TWO);
+           return;
+           }
+
+       var mergePartsPopup = new dijit.Dialog({title: 
+               dojo.i18n.getLocalization("openils.biblio","biblio_messages").MERGE_PARTS_TITLE}); 
+
+       var mergeDiv = dojo.create("div");
+
+       /* 
+        * Establish handler when an item is clicked upon 
+        */ 
+       mergeDiv.processMerge = function (obj) {
+           mergePartsPopup.hide();
+           dojo.require('openils.PermaCrud');  
+
+            var searchParams = {};
+            searchParams["part"] = new Array() ; 
+
+            /*
+             * Establish a list of id's of monograph_parts that are affected by remap. Later, find
+             * all copy_part_map items that reference any of these parts 
+             */
+
+           dojo.forEach (this.items, 
+               function(item) { 
+                      searchParams["part"].push(String(item.id))       /* Must be String in json */
+               });
+           // var testString = searchParams["part"].join(', ');        /* DEBUG */
+
+           var pcrud = new openils.PermaCrud();
+            var cpmList = pcrud.search("acpm", searchParams);
+
+           dojo.forEach(cpmList,
+                   function (g) {
+                       g.part(parseInt(obj.itemID))            /* Assign "winner" DB id of mono_part. */
+                   });
+
+           if (cpmList.length > 0) {
+               pcrud.update( cpmList, {});
+           }
+           /*
+            * Close the connection and commit the transaction.  This is necessary to do before
+            * the subsequent delete operation (because of ON DELETE CASCADE issues).
+            */
+
+          pcrud.disconnect(); 
+
+           /*
+            * Update the AutoGrid to delete the items being mapped out of existence so that 
+            * the display reflects the updated situation.
+            * Then use a PermaCrud connection to delete/eliminate the object.  This
+            * code is adapted from the delete case in AutoGrid.js. Note that this code
+            * uses a total==1 as the exit condition (because you are not deleting the 
+            * winning/prevailing/surviving part.
+            */
+
+          dojo.forEach (items, 
+               function(item) { 
+                  if (item.id != parseInt(obj.itemID)) {
+                     var fmObject = new fieldmapper[gridControl.fmClass]().fromStoreItem(item);
+                     new openils.PermaCrud()['eliminate'](
+                            fmObject, {
+                                oncomplete : function(r) {
+                                    gridControl.store.deleteItem(item);
+                                    if (--total == 1 && gridControl.onPostSubmit) {
+                                        gridControl.onPostSubmit();
+                                    }
+                                }
+                            }
+                      );
+                   }
+                }
+           );
+
+       };
+
+       mergeDiv.innerHTML = "<div class=\"biblio-merge-prevail-title\" >" +
+                              dojo.i18n.getLocalization("openils.biblio","biblio_messages").CLICK_PREVAILING +
+                              "</div>";  
+       mergeDiv.items = items;
+
+        /* 
+         * Create a DIV for each selected item, and put in the container DIV
+         */
+       for (var i = 0; i < total; i++) {
+           var newDiv = dojo.create("div"); 
+           newDiv.className = "biblio-merge-item";
+           newDiv.itemID = items[i].id;
+           newDiv.onclick = function() {mergeDiv.processMerge(this);};
+           var newText = new String(items[i].label);
+            
+            /* To make spacing more visible, replace spaces with a middot character */
+           newText = newText.replace(/ /g, String.fromCharCode(183) /* middot*/);
+           newDiv.appendChild(document.createTextNode( newText ));  
+           mergeDiv.appendChild(newDiv);
+       }
+       mergePartsPopup.setContent(mergeDiv); 
+
+       mergePartsPopup.show();
+
+    };
+
+}
diff --git a/Open-ILS/web/js/dojo/openils/biblio/nls/biblio_messages.js b/Open-ILS/web/js/dojo/openils/biblio/nls/biblio_messages.js
new file mode 100755 (executable)
index 0000000..0ad0af3
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "SELECT_TWO" : "Select two or more items to merge",
+    "MERGE_PARTS_TITLE" : "Merge parts",
+    "CLICK_PREVAILING" : "Click on prevailing item"
+}
diff --git a/docs/RELEASE_NOTES_NEXT/Cataloging/merge-parts.txt b/docs/RELEASE_NOTES_NEXT/Cataloging/merge-parts.txt
new file mode 100644 (file)
index 0000000..e0a9f57
--- /dev/null
@@ -0,0 +1,17 @@
+New Feature: Monograph Part Merging
+===================================
+
+The monograph part list for a bibliographic record may, over time, diverge from the
+proscribed format, resulting in multiple labels for what are essentially the same
+item.  For instance, ``++Vol.{nbsp}1++'' may have variants like ``++V.1++'', ``++Vol{nbsp}1++'', or ``++{nbsp}Vol.{nbsp}1++'' (leading space).
+This feature will allow catalog staff to collapse the variants into one value.
+
+In the Monograph Parts display:
+
+. Click the checkbox for all items you wish to merge including the one you wish to prevail when done.
+. Click on the ``Merge Selected'' button. A pop-up window will list the selected items in
+a monospaced font, with blanks represented by a middle-dot character for more visibility.
+. Click on the item you wish to prevail.
+
+The undesired part labels will be deleted, and any copies that previously used those labels will 
+now use the prevailing label.