From: senator Date: Tue, 16 Feb 2010 15:00:34 +0000 (+0000) Subject: Acq: Several improvements to the LI search interface X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=e016b19c362fcde3bbc3056fd30af759911058eb;p=contrib%2FConifer.git Acq: Several improvements to the LI search interface - You can export a file of ISBN numbers from any interface using an LI table. - You can use multiple search terms in the LI search interface. - The LI search interface no longer returns too many results in tested cases. - You can make a brief bib record out of your LI search terms. - Little bit of misc cleanup. git-svn-id: svn://svn.open-ils.org/ILS/trunk@15545 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Acq/Lineitem.pm b/Open-ILS/src/perlmods/OpenILS/Application/Acq/Lineitem.pm index d6f0076a55..499fb14d68 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Acq/Lineitem.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Acq/Lineitem.pm @@ -310,7 +310,9 @@ sub lineitem_search_by_attributes { my $po_agencies = $search->{po_agencies}; # XXX if none, base it on perms my $query = { - "select" => {"acqlia" => ["lineitem"]}, + "select" => {"acqlia" => + [{"column" => "lineitem", "transform" => "distinct"}] + }, "from" => { "acqlia" => { "acqliad" => {"field" => "id", "fkey" => "definition"}, diff --git a/Open-ILS/web/css/skin/default/acq.css b/Open-ILS/web/css/skin/default/acq.css index 9c1c250f74..78f6a112ae 100644 --- a/Open-ILS/web/css/skin/default/acq.css +++ b/Open-ILS/web/css/skin/default/acq.css @@ -118,6 +118,8 @@ input.oils-acq-li-search { margin: 0 12px; } label.oils-acq-li-search { margin: 0 12px; } span.oils-acq-li-search { margin: 0 12px; } span#records-up { font-weight: bold; } +label[for="attr_search_type_scalar"] { vertical-align: top; } +.oils-acq-li-search-scalar { margin: 8px 0; } #acq-lit-table {width:100%} #acq-lit-table th {padding:5px; font-weight: bold; text-align:left;} diff --git a/Open-ILS/web/js/dojo/openils/XUL.js b/Open-ILS/web/js/dojo/openils/XUL.js index 6a39d1949e..c93cca2df8 100644 --- a/Open-ILS/web/js/dojo/openils/XUL.js +++ b/Open-ILS/web/js/dojo/openils/XUL.js @@ -66,6 +66,10 @@ if(!dojo._hasResource["openils.XUL"]) { "iface": Components.interfaces.nsIScriptableInputStream, "cls": "@mozilla.org/scriptableinputstream;1" }, + "FOS": { + "iface": Components.interfaces.nsIFileOutputStream, + "cls": "@mozilla.org/network/file-output-stream;1" + }, "create": function(key) { return Components.classes[this[key].cls]. createInstance(this[key].iface); @@ -75,33 +79,50 @@ if(!dojo._hasResource["openils.XUL"]) { } }; - openils.XUL.contentFromFileOpenDialog = function(windowTitle) { - try { - var api = new openils.XUL.SimpleXPCOM(); + openils.XUL.contentFromFileOpenDialog = function(windowTitle, sizeLimit) { + var api = new openils.XUL.SimpleXPCOM(); - /* The following enablePrivilege() call must happen at this exact - * level of scope -- not wrapped in another function -- otherwise - * it doesn't work. */ - api.getPrivilegeManager().enablePrivilege("UniversalXPConnect"); + /* The following enablePrivilege() call must happen at this exact + * level of scope -- not wrapped in another function -- otherwise + * it doesn't work. */ + api.getPrivilegeManager().enablePrivilege("UniversalXPConnect"); - var picker = api.create("FP"); - picker.init( - window, windowTitle || "Upload File", api.FP.iface.modeOpen - ); - if (picker.show() == api.FP.iface.returnOK && picker.file) { - var fis = api.create("FIS"); - var sis = api.create("SIS"); + var picker = api.create("FP"); + picker.init( + window, windowTitle || "Upload File", api.FP.iface.modeOpen + ); + if (picker.show() == api.FP.iface.returnOK && picker.file) { + var fis = api.create("FIS"); + var sis = api.create("SIS"); - fis.init(picker.file, 1 /* MODE_RDONLY */, 0, 0); - sis.init(fis); + fis.init(picker.file, 1 /* MODE_RDONLY */, 0, 0); + sis.init(fis); - return sis.read(-1); - } else { - return null; - } - } catch(E) { - alert(E); + return sis.read(sizeLimit || -1); + } else { return null; } }; + + openils.XUL.contentToFileSaveDialog = function(content, windowTitle) { + var api = new openils.XUL.SimpleXPCOM(); + api.getPrivilegeManager().enablePrivilege("UniversalXPConnect"); + + var picker = api.create("FP"); + picker.init( + window, windowTitle || "Save File", api.FP.iface.modeSave + ); + var result = picker.show(); + if (picker.file && + (result == api.FP.iface.returnOK || + result == api.FP.iface.returnReplace)) { + if (!picker.file.exists()) + picker.file.create(0, 0644); /* XXX hardcoded = bad */ + var fos = api.create("FOS"); + fos.init(picker.file, 42 /* WRONLY | CREAT | TRUNCATE */, 0644, 0); + return fos.write(content, content.length); + } else { + return 0; + } + }; } diff --git a/Open-ILS/web/js/dojo/openils/acq/nls/acq.js b/Open-ILS/web/js/dojo/openils/acq/nls/acq.js index 9077dfa5a2..f3f903ff2a 100644 --- a/Open-ILS/web/js/dojo/openils/acq/nls/acq.js +++ b/Open-ILS/web/js/dojo/openils/acq/nls/acq.js @@ -14,5 +14,8 @@ 'LI_ATTR_SEARCH_CHOOSE_FILE': "Select file with search terms", 'LI_ATTR_SEARCH_TOO_LARGE': "That file is too large for this operation.", 'SELECT_AN_LI_ATTRIBUTE': "You must select an LI attribute.", - 'NO_RESULTS': "No results." + 'NO_RESULTS': "No results.", + 'ISBN_SAVE_DIALOG_TITLE': "Save ISBNs to a file", + 'ISBN_SHORT_LIST': "Not all of the selected items had ISBNs.\nChoose OK to save the ISBNs that could be found.", + 'ISBN_EMPTY_LIST': "No ISBNs found." } diff --git a/Open-ILS/web/js/ui/default/acq/common/li_table.js b/Open-ILS/web/js/ui/default/acq/common/li_table.js index 03ba892d9a..bed0e8fb3c 100644 --- a/Open-ILS/web/js/ui/default/acq/common/li_table.js +++ b/Open-ILS/web/js/ui/default/acq/common/li_table.js @@ -1023,6 +1023,10 @@ function AcqLiTable() { this.createAssets(); break; + case 'export_isbn_list': + this.exportISBNList(); + break; + case 'add_brief_record': if(this.isPO) location.href = oilsBasePath + '/acq/picklist/brief_record?po=' + this.isPO; @@ -1048,6 +1052,36 @@ function AcqLiTable() { ); } + /* Should really think about generalizing this to do more than ISBN #s */ + this.exportISBNList = function() { + var selected = this.getSelected(); + var isbn_list = selected.map( + function(li) { + return (new openils.acq.Lineitem({"lineitem": li})).findAttr( + "isbn", "lineitem_marc_attr_definition" + ); + } + ).filter(function(attr) { return Boolean(attr); }); + + if (isbn_list.length > 0) { + if (isbn_list.length < selected.length) { + if (!confirm(localeStrings.ISBN_SHORT_LIST)) { + return; + } + } + try { + openils.XUL.contentToFileSaveDialog( + isbn_list.join("\n"), + localeStrings.ISBN_SAVE_DIALOG_TITLE + ); + } catch (E) { + alert(E); + } + } else { + alert(localeStrings.ISBN_EMPTY_LIST); + } + }; + this.printPO = function() { if(!this.isPO) return; progressDialog.show(true); diff --git a/Open-ILS/web/js/ui/default/acq/lineitem/search.js b/Open-ILS/web/js/ui/default/acq/lineitem/search.js index 00f4ba4abd..e1aedf86ca 100644 --- a/Open-ILS/web/js/ui/default/acq/lineitem/search.js +++ b/Open-ILS/web/js/ui/default/acq/lineitem/search.js @@ -12,6 +12,7 @@ dojo.require("openils.widget.AutoFieldWidget"); var _searchable_by_array = ["issn", "isbn", "upc"]; var combinedAttrValueArray = []; +var scalarAttrSearchManager; var liTable; function prepareStateStore(pcrud) { @@ -30,11 +31,6 @@ function prepareStateStore(pcrud) { } function prepareScalarSearchStore(pcrud) { - attrScalarDefSelector.store = new dojo.data.ItemFileReadStore({ - "data": acqliad.toStoreData( - pcrud.search("acqliad", {"id": {"!=": null}}) - ) - }); } function prepareArraySearchStore(pcrud) { @@ -55,25 +51,14 @@ function prepareAgencySelector() { }).build(); } -function load() { - var pcrud = new openils.PermaCrud(); - - prepareStateStore(pcrud); - prepareScalarSearchStore(pcrud); - prepareArraySearchStore(pcrud); - - prepareAgencySelector(); - - liTable = new AcqLiTable(); - openils.Util.show("oils-acq-li-search-form-holder"); -} - function toggleAttrSearchType(which, checked) { /* This would be cooler with a slick dispatch table instead of branchy * logic, but whatever... */ if (checked) { if (which == "scalar") { - openils.Util.show("oils-acq-li-search-attr-scalar", "inline"); + if (scalarAttrSearchManager.index < 1) + scalarAttrSearchManager.add(); + openils.Util.show("oils-acq-li-search-attr-scalar", "inline-block"); openils.Util.hide("oils-acq-li-search-attr-array"); } else if (which == "array") { openils.Util.hide("oils-acq-li-search-attr-scalar"); @@ -96,16 +81,14 @@ var buildAttrSearchClause = { }; }, "scalar": function(v) { - if (!v.scalar_def) { + var r = scalarAttrSearchManager.buildSearchClause(); + if (r.attr_value_pairs.length < 1) { throw new Error(localeStrings.SELECT_AN_LI_ATTRIBUTE); + } else { + return r; } - return { - "attr_value_pairs": - [[Number(v.scalar_def), v.scalar_value]] /* [[sic]] */ - }; }, "none": function(v) { - //return {"attr_value_pairs": [[1, ""]]}; return {}; } }; @@ -120,18 +103,19 @@ function clearTerms() { } function loadTermsFromFile() { - var rawdata = openils.XUL.contentFromFileOpenDialog( - localeStrings.LI_ATTR_SEARCH_CHOOSE_FILE - ); - if (!rawdata) { - return; - } else if (rawdata.length > 1024 * 128) { + var rawdata; + + try { /* FIXME 128k is completely arbitrary; needs researched for - * a sane limit and should also be made configurable. Further, if - * there's going to be a size limit, it'd be much better to apply - * it before reading in the file at all, not now. */ - alert(localeStrings.LI_ATTR_SEARCH_TOO_LARGE); - } else { + * a sane limit and should also be made configurable. */ + rawdata = openils.XUL.contentFromFileOpenDialog( + localeStrings.LI_ATTR_SEARCH_CHOOSE_FILE, 1024 * 128 + ); + } catch (E) { + alert(E); + } + + if (rawdata) { try { combinedAttrValueArray = combinedAttrValueArray.concat(naivelyParse(rawdata)); @@ -187,4 +171,87 @@ function doSearch(values) { } } +function myScalarAttrSearchManager(template_id, pcrud) { + this.template = dojo.byId(template_id); + this.store = new dojo.data.ItemFileReadStore({ + "data": acqliad.toStoreData( + pcrud.search("acqliad", {"id": {"!=": null}}) + ) + }); + this.rows = {}; + this.index = 0; +}; +myScalarAttrSearchManager.prototype.remove = function(n) { + dojo.destroy("scalar_attr_holder_" + n); + delete this.rows[n]; +}; +myScalarAttrSearchManager.prototype.add = function() { + var self = this; + var n = this.index; + var clone = dojo.clone(this.template); + var def = dojo.query('input[name="def"]', clone)[0]; + var value = dojo.query('input[name="value"]', clone)[0]; + var a = dojo.query('a', clone)[0]; + + clone.id = "scalar_attr_holder_" + n; + a.onclick = function() { self.remove(n); }; + + this.rows[n] = [ + new dijit.form.FilteringSelect({ + "id": "scalar_def_" + n, + "name": "scalar_def_" + n, + "store": this.store, + "labelAttr": "description", + "searchAttr": "description" + }, def), + new dijit.form.TextBox({ + "id": "scalar_value_" + n, + "name": "scalar_value_" + n + }, value) + ]; + + this.index++; + + dojo.place(clone, "oils-acq-li-search-scalar-adder", "before"); + openils.Util.show(clone); +}; +myScalarAttrSearchManager.prototype.buildSearchClause = function() { + var list = []; + for (var k in this.rows) { + var def = this.rows[k][0].attr("value"); + var val = this.rows[k][1].attr("value"); + if (def != "" && val != "") + list.push([Number(def), val]); + } + return {"attr_value_pairs": list}; +}; +myScalarAttrSearchManager.prototype.simplifiedPairs = function() { + var result = {}; + for (var k in this.rows) { + result[this.rows[k][0].attr("value")] = this.rows[k][1].attr("value"); + } + return result; +}; +myScalarAttrSearchManager.prototype.newBrief = function() { + location.href = oilsBasePath + "/acq/picklist/brief_record?prepop=" + + encodeURIComponent(js2JSON(this.simplifiedPairs())); +}; + + +function load() { + var pcrud = new openils.PermaCrud(); + + prepareStateStore(pcrud); + prepareArraySearchStore(pcrud); + + prepareAgencySelector(); + + liTable = new AcqLiTable(); + scalarAttrSearchManager = new myScalarAttrSearchManager( + "oils-acq-li-search-scalar-template", pcrud + ); + + openils.Util.show("oils-acq-li-search-form-holder"); +} + openils.Util.addOnLoad(load); diff --git a/Open-ILS/web/js/ui/default/acq/picklist/brief_record.js b/Open-ILS/web/js/ui/default/acq/picklist/brief_record.js index 66ebc67c5b..d5ea700437 100644 --- a/Open-ILS/web/js/ui/default/acq/picklist/brief_record.js +++ b/Open-ILS/web/js/ui/default/acq/picklist/brief_record.js @@ -21,6 +21,7 @@ function drawBriefRecordForm(fields) { var cgi = new openils.CGI(); paramPL = cgi.param('pl'); paramPO = cgi.param('po'); + prepop = JSON2js(cgi.param('prepop')); if(paramPL) { @@ -63,9 +64,11 @@ function drawBriefRecordForm(fields) { } + /* marcEditButton.onClick = function(fields) { saveBriefRecord(fields, true); } + */ fieldmapper.standardRequest( ['open-ils.acq', 'open-ils.acq.lineitem_attr_definition.retrieve.all'], @@ -90,7 +93,12 @@ function drawBriefRecordForm(fields) { attrDefs[def.code()] = xpathParser.parse(def.xpath()); var row = rowTmpl.cloneNode(true); dojo.query('[name=name]', row)[0].innerHTML = def.description(); - new dijit.form.TextBox({name : def.code()}, dojo.query('[name=widget]', row)[0]); + var textbox = new dijit.form.TextBox( + {"name": def.code()}, + dojo.query('[name=widget]', row)[0] + ); + if (prepop && prepop[def.id()]) + textbox.attr("value", prepop[def.id()]); tbody.appendChild(row); } ); diff --git a/Open-ILS/web/templates/default/acq/common/li_table.tt2 b/Open-ILS/web/templates/default/acq/common/li_table.tt2 index 0787eb3070..5b8fceff10 100644 --- a/Open-ILS/web/templates/default/acq/common/li_table.tt2 +++ b/Open-ILS/web/templates/default/acq/common/li_table.tt2 @@ -18,6 +18,7 @@ + diff --git a/Open-ILS/web/templates/default/acq/lineitem/search.tt2 b/Open-ILS/web/templates/default/acq/lineitem/search.tt2 index 1c2739b374..0195817788 100644 --- a/Open-ILS/web/templates/default/acq/lineitem/search.tt2 +++ b/Open-ILS/web/templates/default/acq/lineitem/search.tt2 @@ -35,20 +35,52 @@
+