From f714b9a6afe913f9d4d42cd69b604d0fe57c1a09 Mon Sep 17 00:00:00 2001 From: senator Date: Tue, 13 Apr 2010 20:17:55 +0000 Subject: [PATCH] Acq: add search-by-file-of-terms to unified search This feature was present in the now deprecated lineitem search interface. It works, but it is severely limited at the moment in the number of search terms you can use, because search queries are made into URIs before the search actually happens (in order to make backing up to old search results possible), and at a certain point, very large URIs become infeasible. Some adjustments to how search history is made naviagable may be in order. git-svn-id: svn://svn.open-ils.org/ILS/trunk@16230 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/web/css/skin/default/acq.css | 11 +- .../web/js/dojo/openils/widget/XULTermLoader.js | 96 ++++++++++++++++++ .../js/dojo/openils/widget/nls/XULTermLoader.js | 7 ++ Open-ILS/web/js/ui/default/acq/search/unified.js | 111 ++++++++++++++------- .../web/templates/default/acq/search/unified.tt2 | 14 ++- 5 files changed, 193 insertions(+), 46 deletions(-) create mode 100644 Open-ILS/web/js/dojo/openils/widget/XULTermLoader.js create mode 100644 Open-ILS/web/js/dojo/openils/widget/nls/XULTermLoader.js diff --git a/Open-ILS/web/css/skin/default/acq.css b/Open-ILS/web/css/skin/default/acq.css index 6cbc1fa3db..96eaeaad28 100644 --- a/Open-ILS/web/css/skin/default/acq.css +++ b/Open-ILS/web/css/skin/default/acq.css @@ -202,6 +202,12 @@ span[name="notes_alert_flag"] {color: #c00;font-weight: bold;font-size: 110%;mar #acq-invoice-new-msg { font-weight: bold; margin: 10px;} #acq-invoice-li-details { padding: 10px; font-weight: bold; border: 1px solid #888; margin: 10px; } #acq-invoice-create { margin: 10px; } +.acq-inoice-item-extra-info { padding-left: 10px; } +.acq-inoice-item-info { font-weight: bold; } +.acq-invoice-row td { border-bottom: 1px solid #e0e0e0; } +.acq-invoice-invalid-amount input { color: red; font-weight: bold; } +.acq-link-invoice-dialog td,.acq-link-invoice-dialog th {padding-top: 10px;} + #acq-unified-heading { margin-bottom: 10px; } #acq-unified-heading-actual { float: left; width: 50%; font-size: 120%; font-weight: bold; } #acq-unified-heading-controls { float: right; width: 50%; text-align: right; } @@ -218,8 +224,3 @@ option[disabled="disabled"] { font-style: italic; } .acq-unified-terms-match { width: 15%; } .acq-unified-terms-remove { width: 5%; text-align: right; } .acq-unified-remover { color: #c00; } -.acq-inoice-item-extra-info { padding-left: 10px; } -.acq-inoice-item-info { font-weight: bold; } -.acq-invoice-row td { border-bottom: 1px solid #e0e0e0; } -.acq-invoice-invalid-amount input { color: red; font-weight: bold; } -.acq-link-invoice-dialog td,.acq-link-invoice-dialog th {padding-top: 10px;} diff --git a/Open-ILS/web/js/dojo/openils/widget/XULTermLoader.js b/Open-ILS/web/js/dojo/openils/widget/XULTermLoader.js new file mode 100644 index 0000000000..fb4770d4ef --- /dev/null +++ b/Open-ILS/web/js/dojo/openils/widget/XULTermLoader.js @@ -0,0 +1,96 @@ +if (!dojo._hasResource["openils.widget.XULTermLoader"]) { + dojo._hasResource["openils.widget.XULTermLoader"] = true; + + dojo.provide("openils.widget.XULTermLoader"); + dojo.require("openils.XUL"); + dojo.requireLocalization("openils.widget", "XULTermLoader"); + + dojo.declare( + "openils.widget.XULTermLoader", [dijit.layout.ContentPane], { + "constructor": function(args) { + this.args = args; + this.terms = []; + this._ = openils.widget.XULTermLoader.localeStrings; + + /* XXX Totally arbitrary defaults. Keeping them low for now + * since all search terms have to be turned into a URL. + * There will need to be a better long term solution. + */ + if (!this.args.fileSizeLimit) + this.args.fileSizeLimit = 2048; + if (!this.args.termLimit) + this.args.termLimit = 100; + }, + "build": function(callback) { + var self = this; + + this.domNode = dojo.create("span"); + this.labelNode = dojo.create( + "span", { + "innerHTML": this._.LABEL_TEXT, + "style": "padding-right: 8px;" + }, this.domNode, "last" + ); + this.countNode = dojo.create( + "span", {"innerHTML": this.terms.length}, + this.labelNode, "first" + ); + this.buttonNode = dojo.create( + "button", { + "innerHTML": this._.BUTTON_TEXT, + "onclick": function() { self.loadTerms(); } + }, + this.domNode, "last" + ); + + if (this.args.parentNode) + dojo.place(this.domNode, this.args.parentNode, "last"); + + callback(this); + }, + "updateCount": function() { + var value = this.attr("value"); + if (dojo.isArray(value)) + this.terms = this.attr("value"); + this.countNode.innerHTML = this.terms.length; + }, + "focus": function() { + this.buttonNode.focus(); + }, + "loadTerms": function() { + try { + if (this.terms.length >= this.args.termLimit) { + alert(this._.TERM_LIMIT); + return; + } + var data = this.parseUnimaginatively( + openils.XUL.contentFromFileOpenDialog( + this._.CHOOSE_FILE, this.args.fileSizeLimit + ) + ); + if (data.length + this.terms.length >= + this.args.termLimit) { + alert(this._.TERM_LIMIT_SOME); + var can = this.args.termLimit - this.terms.length; + if (can > 0) + this.terms = this.terms.concat(data.slice(0, can)); + } else { + this.terms = this.terms.concat(data); + } + this.attr("value", this.terms); + this.updateCount(); + } catch(E) { + alert(E); + } + }, + "parseUnimaginatively": function(data) { + return data.split(/[\n, ]/).filter( + function(o) { return o.length > 0; } + ); + } + } + ); + + openils.widget.XULTermLoader.localeStrings = + dojo.i18n.getLocalization("openils.widget", "XULTermLoader"); +} diff --git a/Open-ILS/web/js/dojo/openils/widget/nls/XULTermLoader.js b/Open-ILS/web/js/dojo/openils/widget/nls/XULTermLoader.js new file mode 100644 index 0000000000..71881a382a --- /dev/null +++ b/Open-ILS/web/js/dojo/openils/widget/nls/XULTermLoader.js @@ -0,0 +1,7 @@ +{ + 'LABEL_TEXT': " term(s) loaded", + 'BUTTON_TEXT': "Load more terms", + 'TERM_LIMIT': "You have already loaded the maximum number of terms.", + 'TERM_LIMIT_SOME': "Could not load all terms from the file without exceeding maximum number of terms. Some data not included.", + 'CHOOSE_FILE': "Choose a file from which to read search terms." +} diff --git a/Open-ILS/web/js/ui/default/acq/search/unified.js b/Open-ILS/web/js/ui/default/acq/search/unified.js index 1413238127..67114c1c73 100644 --- a/Open-ILS/web/js/ui/default/acq/search/unified.js +++ b/Open-ILS/web/js/ui/default/acq/search/unified.js @@ -2,6 +2,7 @@ dojo.require("dojo.date.stamp"); dojo.require("dojox.encoding.base64"); dojo.require("openils.widget.AutoGrid"); dojo.require("openils.widget.AutoWidget"); +dojo.require("openils.widget.XULTermLoader"); dojo.require("openils.PermaCrud"); var termSelectorFactory; @@ -19,7 +20,7 @@ HTMLSelectElement.prototype.getValue = function() { /* only sets the selected value if such an option is actually available */ HTMLSelectElement.prototype.setValue = function(s) { for (var i = 0; i < this.options.length; i++) { - if (s == this.options[i].value && !this.options[i].disabled) { + if (s == this.options[i].value) { this.selectedIndex = i; break; } @@ -88,26 +89,47 @@ function TermSelectorFactory(terms) { "datatype": self.terms[parts[0]][parts[1]].datatype }; }, - "makeWidget": function(parentNode, wStore, callback) { + "makeWidget": function(parentNode, wStore, matchHow, value, callback) { var term = this.getTerm(); var widgetKey = this.uniq; - if (term.hint == "acqlia") { + if (matchHow.getValue() == "__in") { + new openils.widget.XULTermLoader({ + "parentNode": parentNode + }).build( + function(w) { + wStore[widgetKey] = w; + if (typeof(callback) == "function") + callback(term, widgetKey); + if (typeof(value) != "undefined") + w.attr("value", value); + /* I would love for the following call not to be + * necessary, so that updating the value of the dijit + * would lead to this automatically, but I can't yet + * figure out the correct way to do this in Dojo. + */ + w.updateCount(); + } + ); + } else if (term.hint == "acqlia") { wStore[widgetKey] = dojo.create( "input", {"type": "text"}, parentNode, "only" ); + if (typeof(value) != "undefined") + wStore[widgetKey].attr("value", value); wStore[widgetKey].focus(); if (typeof(callback) == "function") callback(term, widgetKey); } else { - var widget = new openils.widget.AutoFieldWidget({ + new openils.widget.AutoFieldWidget({ "fmClass": term.hint, "fmField": term.field, "noDisablePkey": true, "parentNode": dojo.create("span", null, parentNode, "only") - }); - widget.build( + }).build( function(w) { wStore[widgetKey] = w; + if (typeof(value) != "undefined") + w.attr("value", value); w.focus(); if (typeof(callback) == "function") callback(term, widgetKey); @@ -199,6 +221,40 @@ function TermManager() { this._selector = function(id) { return dojo.byId("term-" + id); }; this._match_how = function(id) { return dojo.byId("term-match-" + id); }; + this._updateMatchHowForField = function(term, key) { + /* NOTE important to use self, not this, in this function. + * + * Based on the selected field (its datatype and the kind of widget + * that AutoFieldWidget provides for it) we update the possible + * choices in the mach_how selector. + */ + var w = self.widgets[key]; + var can_do_fuzzy, can_do_in; + if (term.datatype == "id") { + can_do_fuzzy = false; + can_do_in = true; + } else if (term.datatype == "link") { + can_do_fuzzy = (self.getLinkTarget(term) == "au"); + can_do_in = false; /* XXX might revise later */ + } else if (typeof(w.declaredClass) != "undefined") { + can_do_fuzzy = can_do_in = + Boolean(w.declaredClass.match(/form\.Text/)); + } else { + var type = dojo.attr(w, "type"); + if (type) + can_do_fuzzy = can_do_in = (type == "text"); + else + can_do_fuzzy = can_do_in = false; + } + + self.matchHowAllow(key, "__fuzzy", can_do_fuzzy); + self.matchHowAllow(key, "__in", can_do_in); + + var inequalities = (term.datatype == "timestamp"); + self.matchHowAllow(key, "__gte", inequalities); + self.matchHowAllow(key, "__lte", inequalities); + }; + this.removerButton = function(n) { return dojo.create("button", { "innerHTML": "X", @@ -225,36 +281,8 @@ function TermManager() { dojo.empty(where); this._selector(id).makeWidget( - where, this.widgets, - function(term, key) { - var w = self.widgets[key]; - var can_do_fuzzy; - if (term.datatype == "id") { - can_do_fuzzy = false; - } else if (term.datatype == "link") { - can_do_fuzzy = (self.getLinkTarget(term) == "au"); - } else if (typeof(w.declaredClass) != "undefined") { - can_do_fuzzy = Boolean(w.declaredClass.match(/form\.Text/)); - } else { - var type = dojo.attr(w, "type"); - if (type) - can_do_fuzzy = (type == "text"); - else - can_do_fuzzy = false; - } - self.matchHowAllow(id, "__fuzzy", can_do_fuzzy); - - var inequalities = (term.datatype == "timestamp"); - self.matchHowAllow(id, "__gte", inequalities); - self.matchHowAllow(id, "__lte", inequalities); - - if (value) { - if (typeof(w.attr) == "function") - w.attr("value", value); - else - w.value = value; - } - } + where, this.widgets, this._match_how(id), value, + this._updateMatchHowForField ); }; @@ -308,6 +336,13 @@ function TermManager() { dojo.attr( match_how, "onchange", function() { + if (this.getValue() == "__in") { + self.updateRowWidget(uniq); + this.was_in = true; + } else if (this.was_in) { + self.updateRowWidget(uniq); + this.was_in = false; + } if (self.widgets[uniq]) self.widgets[uniq].focus(); } ); @@ -320,11 +355,13 @@ function TermManager() { if (term && hint) { var field = hint + ":" + this._term_reverse_selector_field(term); selector.setValue(field); - this.updateRowWidget(uniq, this._term_reverse_selector_value(term)); var match_how_value = this._term_reverse_match_how(term); if (match_how_value) match_how.setValue(match_how_value); + + this.updateRowWidget(uniq, this._term_reverse_selector_value(term)); + } } diff --git a/Open-ILS/web/templates/default/acq/search/unified.tt2 b/Open-ILS/web/templates/default/acq/search/unified.tt2 index dcc14fc7c6..f5722e28f1 100644 --- a/Open-ILS/web/templates/default/acq/search/unified.tt2 +++ b/Open-ILS/web/templates/default/acq/search/unified.tt2 @@ -106,16 +106,22 @@