Index: umc/python/pkgdb/de.po =================================================================== --- umc/python/pkgdb/de.po (revision 4255) +++ umc/python/pkgdb/de.po (working copy) @@ -12,167 +12,166 @@ "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: umc/python/pkgdb/__init__.py:200 +#: umc/python/pkgdb/__init__.py:205 msgid "Compared to version" msgstr "Verglichen mit Version" -#: umc/python/pkgdb/__init__.py:227 +#: umc/python/pkgdb/__init__.py:232 msgid "Deinstall" msgstr "Deinstall" -#: umc/python/pkgdb/__init__.py:219 +#: umc/python/pkgdb/__init__.py:224 msgid "Domaincontroller Backup" msgstr "Domaincontroller Backup" -#: umc/python/pkgdb/__init__.py:218 +#: umc/python/pkgdb/__init__.py:223 msgid "Domaincontroller Master" msgstr "Domaincontroller Master" -#: umc/python/pkgdb/__init__.py:220 +#: umc/python/pkgdb/__init__.py:225 msgid "Domaincontroller Slave" msgstr "Domaincontroller Slave" -#: umc/python/pkgdb/__init__.py:196 +#: umc/python/pkgdb/__init__.py:201 msgid "Find packages installed incompletely" msgstr "Unvollständig installierte Pakete" -#: umc/python/pkgdb/__init__.py:226 +#: umc/python/pkgdb/__init__.py:231 msgid "Hold" msgstr "Hold" -#: umc/python/pkgdb/__init__.py:234 +#: umc/python/pkgdb/__init__.py:239 msgid "Hold + Reinstall required" msgstr "Hold + Reinstall required" -#: umc/python/pkgdb/__init__.py:222 +#: umc/python/pkgdb/__init__.py:227 msgid "IP-managed Client" msgstr "IP-managed Client" -#: umc/python/pkgdb/__init__.py:225 +#: umc/python/pkgdb/__init__.py:230 msgid "Install" msgstr "Install" -#: umc/python/pkgdb/__init__.py:197 +#: umc/python/pkgdb/__init__.py:202 msgid "Installation state" msgstr "Installationsstatus" -#: umc/python/pkgdb/__init__.py:198 +#: umc/python/pkgdb/__init__.py:203 msgid "Inventory date" msgstr "Erfassungsdatum" -#: umc/python/pkgdb/__init__.py:221 +#: umc/python/pkgdb/__init__.py:226 msgid "Member Server" msgstr "Member Server" -#: umc/python/pkgdb/__init__.py:223 +#: umc/python/pkgdb/__init__.py:228 msgid "Mobile Client" msgstr "Mobile Client" -#: umc/python/pkgdb/__init__.py:231 +#: umc/python/pkgdb/__init__.py:236 msgid "OK" msgstr "OK" -#: umc/python/pkgdb/__init__.py:201 +#: umc/python/pkgdb/__init__.py:206 msgid "Package name" msgstr "Paketname" -#: umc/python/pkgdb/__init__.py:203 +#: umc/python/pkgdb/__init__.py:208 msgid "Package state" msgstr "Paketstatus" -#: umc/python/pkgdb/__init__.py:202 +#: umc/python/pkgdb/__init__.py:207 msgid "Package version" msgstr "Paketversion" -#: umc/python/pkgdb/__init__.py:228 +#: umc/python/pkgdb/__init__.py:233 msgid "Purge" msgstr "Purge" -#: umc/python/pkgdb/__init__.py:232 +#: umc/python/pkgdb/__init__.py:237 msgid "Reinstall required" msgstr "Neuinstallation erforderlich" -#: umc/python/pkgdb/__init__.py:204 +#: umc/python/pkgdb/__init__.py:209 msgid "Selection state" msgstr "Selektionsstatus" -#: umc/python/pkgdb/__init__.py:205 +#: umc/python/pkgdb/__init__.py:210 msgid "System name" msgstr "Systemname" -#: umc/python/pkgdb/__init__.py:206 +#: umc/python/pkgdb/__init__.py:211 msgid "System role" msgstr "Systemrolle" -#: umc/python/pkgdb/__init__.py:199 +#: umc/python/pkgdb/__init__.py:204 msgid "Systems not updated" msgstr "Nicht aktualisierte Systeme" -#: umc/python/pkgdb/__init__.py:207 +#: umc/python/pkgdb/__init__.py:212 msgid "UCS Version" msgstr "UCS Version" -#: umc/python/pkgdb/__init__.py:229 +#: umc/python/pkgdb/__init__.py:234 msgid "Unknown" msgstr "Unbekannt" -#: umc/python/pkgdb/__init__.py:241 +#: umc/python/pkgdb/__init__.py:246 msgid "configfiles" msgstr "configfiles" -#: umc/python/pkgdb/__init__.py:212 +#: umc/python/pkgdb/__init__.py:217 msgid "doesn't match" msgstr "entspricht nicht" -#: umc/python/pkgdb/__init__.py:238 +#: umc/python/pkgdb/__init__.py:243 msgid "half-configured" msgstr "halb-konfiguriert" -#: umc/python/pkgdb/__init__.py:240 +#: umc/python/pkgdb/__init__.py:245 msgid "half-installed" msgstr "halb-installiert" -#: umc/python/pkgdb/__init__.py:242 +#: umc/python/pkgdb/__init__.py:247 msgid "installed" msgstr "installiert" -#: umc/python/pkgdb/__init__.py:209 +#: umc/python/pkgdb/__init__.py:214 msgid "is" msgstr "ist" -#: umc/python/pkgdb/__init__.py:214 +#: umc/python/pkgdb/__init__.py:219 msgid "is greater or equal" msgstr "ist größer oder gleich" -#: umc/python/pkgdb/__init__.py:213 +#: umc/python/pkgdb/__init__.py:218 msgid "is greater than" msgstr "ist größer als" -#: umc/python/pkgdb/__init__.py:210 +#: umc/python/pkgdb/__init__.py:215 msgid "is not" msgstr "ist nicht" -#: umc/python/pkgdb/__init__.py:216 +#: umc/python/pkgdb/__init__.py:221 msgid "is smaller or equal" msgstr "ist kleiner oder gleich" -#: umc/python/pkgdb/__init__.py:215 +#: umc/python/pkgdb/__init__.py:220 msgid "is smaller than" msgstr "ist kleiner als" -#: umc/python/pkgdb/__init__.py:211 +#: umc/python/pkgdb/__init__.py:216 msgid "matches" msgstr "entspricht" -#: umc/python/pkgdb/__init__.py:236 +#: umc/python/pkgdb/__init__.py:241 msgid "not installed" msgstr "nicht installiert" -#: umc/python/pkgdb/__init__.py:239 +#: umc/python/pkgdb/__init__.py:244 msgid "uninstalled" msgstr "nicht installiert" -#: umc/python/pkgdb/__init__.py:237 +#: umc/python/pkgdb/__init__.py:242 msgid "unpacked" msgstr "ausgepackt" - Index: umc/python/pkgdb/__init__.py =================================================================== --- umc/python/pkgdb/__init__.py (revision 4255) +++ umc/python/pkgdb/__init__.py (working copy) @@ -131,8 +131,12 @@ 'columns': ['sysname','pkgname','vername','inventory_date', 'selectedstate','inststate','currentstate' ], 'db_fields': ['sysname','pkgname','vername','inventory_date', 'inststatus','selectedstate','inststate','currentstate' ], 'function': 'sql_get_packages_in_systems_by_query', + # They allow querying for the UCS version, and then they don't display it? Who would use that at all? + # But nevertheless, if 'sysversion' is an allowed search key we have to switch this 'Join' flag on, + # or we get always empty result sets. + # (No, this join is not the performance bottleneck, believe me.) 'args': { - 'join_systems': False, + 'join_systems': True, # /usr/share/pyshared/univention/pkgdb.py needs to be patched to understand these args: # # 'limit' ... avoids fetching large amounts of data that can't be processed either @@ -261,6 +265,10 @@ what = 'checking variable lists' self._check_variable_lists() + + # initialize some member variables + self._last_query = 0 + self._last_result = [] except Exception,ex: MODULE.warn("[INIT] while %s: %s" % (what,str(ex))) @@ -300,6 +308,18 @@ for s in st: MODULE.info(" << %s" % s) # ----------------------------------- + + # When a sort header is clicked, the frontend will issue the same query + # again. We're prepared for this, remembering the last query options and + # the result set. + + if cmp(self._last_query,request.options) == 0: + MODULE.info(" ++ Same query: returning same result (%d entries) again." % len(self._last_result)) + result = self._last_result + self.finished(request.id,result) + return + + self._last_query = request.options result = [] @@ -362,6 +382,10 @@ MODULE.warn(" !! execute query: %s" % str(ex)) request.status = SUCCESS + + # Remember result for repeated invocation of the same query + # (e.g. click on any sort header) + self._last_result = result # ---------- DEBUG -------------- MODULE.info("pkgdb/query returns:") @@ -707,7 +731,7 @@ if len(result) > RECORD_LIMIT: what = 'limiting record count' MODULE.warn(" >> QUERY returned %d entries -> showing only first %d" % (len(result),RECORD_LIMIT)) - result = result[0:RECORD_LIMIT] + del result[RECORD_LIMIT:] return result except Exception,ex: MODULE.warn(" !! Query (function='%s',query='%s',args='%s') failed:" % (function,query,args)) Index: umc/js/pkgdb.js =================================================================== --- umc/js/pkgdb.js (revision 4255) +++ umc/js/pkgdb.js (working copy) @@ -42,17 +42,4 @@ } -// startup: function() { -// -// this.inherited(arguments); -// -// }, -// -// uninitialize: function() { -// -// this.inherited(arguments); -// this._refresh_time = 0; -// -// } - }); Index: umc/js/de.po =================================================================== --- umc/js/de.po (revision 4255) +++ umc/js/de.po (working copy) @@ -12,7 +12,7 @@ "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: umc/js/_pkgdb/SearchForm.js:33 +#: umc/js/_pkgdb/SearchForm.js:39 msgid "--- Please select ---" msgstr "--- Bitte wählen ---" @@ -32,7 +32,7 @@ msgid "Inventory date" msgstr "Erfaßt am" -#: umc/js/_pkgdb/SearchForm.js:48 +#: umc/js/_pkgdb/SearchForm.js:52 msgid "Operator" msgstr "Operator" @@ -52,10 +52,8 @@ msgid "Packages" msgstr "Pakete" -#: umc/js/_pkgdb/SearchForm.js:65 -#: umc/js/_pkgdb/SearchForm.js:81 -#: umc/js/_pkgdb/SearchForm.js:335 -#: umc/js/_pkgdb/SearchForm.js:336 +#: umc/js/_pkgdb/SearchForm.js:67 umc/js/_pkgdb/SearchForm.js:81 +#: umc/js/_pkgdb/SearchForm.js:283 msgid "Pattern" msgstr "Suchbegriff" @@ -63,7 +61,7 @@ msgid "Problems" msgstr "Probleme" -#: umc/js/_pkgdb/SearchForm.js:101 +#: umc/js/_pkgdb/SearchForm.js:97 msgid "Search" msgstr "Suchen" @@ -75,7 +73,7 @@ msgid "Search for systems with specific software properties" msgstr "Sucht Systeme mit speziellen Software-Eigenschaften" -#: umc/js/_pkgdb/SearchForm.js:31 +#: umc/js/_pkgdb/SearchForm.js:37 msgid "Search for:" msgstr "Suche nach:" @@ -106,4 +104,3 @@ #: umc/js/_pkgdb/KeyTranslator.js:53 msgid "UCS Version" msgstr "UCS Version" - Index: umc/js/_pkgdb/SearchForm.js =================================================================== --- umc/js/_pkgdb/SearchForm.js (revision 4255) +++ umc/js/_pkgdb/SearchForm.js (working copy) @@ -17,9 +17,15 @@ i18nClass: 'umc.modules.pkgdb', // Some status variables - _patterns_allowed: false, // true if operator is selectable - _pattern_is_list: false, // true if pattern is ComboBox (not TextBox) + _pattern_needed: true, // true if a pattern is required by this key+operator + _pattern_is_list: false, // true if pattern is ComboBox. false if TextBox. + _submit_allowed: false, // true if current input allows SUBMIT (including that no queries are pending) + // true while the corresponding query is pending + _keys_pending: true, + _operators_pending: false, + _proposals_pending: false, + postMixInProperties: function() { dojo.mixin(this,{ @@ -36,9 +42,7 @@ dynamicOptions: {page:this.pageKey}, onDynamicValuesLoaded: dojo.hitch(this, function(values) { this._set_selection_to_first_element('key'); - }), - onChange: dojo.hitch(this, function(value) { - this.onQueryChanged('key',value); + this._set_query_pending('key',false); }) }, { @@ -53,9 +57,7 @@ }), onDynamicValuesLoaded: dojo.hitch(this, function(values) { this._handle_operators(values); - }), - onChange: dojo.hitch(this, function(value) { - this.onQueryChanged('operator',value); + this._set_query_pending('operator',false); }) }, { @@ -70,9 +72,7 @@ }), onDynamicValuesLoaded: dojo.hitch(this, function(values) { this._handle_proposals(values); - }), - onChange: dojo.hitch(this, function(value) { - this.onQueryChanged('pattern_list',value); + this._set_query_pending('proposal',false); }) }, { @@ -80,13 +80,9 @@ name: 'pattern_text', label: this._("Pattern"), style: 'width:350px;', - onChange: dojo.hitch(this, function(value) { - this.onQueryChanged('pattern_text',value); - }), // inherits from dijit.form.ValidationTextBox, so we can use its // validation abilities regExp: '^[A-Za-z0-9_.*?-]+$', // [:alnum:] and these: _ - . * ? - required: true // force nonempty } ], layout: @@ -117,6 +113,31 @@ this.showWidget('pattern_text',false); this.showWidget('pattern_list',false); this.showWidget('operator',false); + + // whenever one of our 'pending' vars is changed... + this.watch('_keys_pending',dojo.hitch(this,function(name,oldval,value) { + this._handle_query_changes(name,value); + })); + this.watch('_operators_pending',dojo.hitch(this,function(name,oldval,value) { + this._handle_query_changes(name,value); + })); + this.watch('_proposals_pending',dojo.hitch(this,function(name,oldval,value) { + this._handle_query_changes(name,value); + })); + + // whenever one of the dialog values is being changed... + dojo.connect(this.getWidget('key'),'onChange',dojo.hitch(this, function(value) { + this._handle_query_changes('key',value); + })); + dojo.connect(this.getWidget('operator'),'onChange',dojo.hitch(this, function(value) { + this._handle_query_changes('operator',value); + })); + dojo.connect(this.getWidget('pattern_text'),'onChange',dojo.hitch(this, function(value) { + this._handle_query_changes('pattern_text',value); + })); + dojo.connect(this.getWidget('pattern_list'),'onChange',dojo.hitch(this, function(value) { + this._handle_query_changes('pattern_list',value); + })); }, // --------------------------------------------------------------------- @@ -158,7 +179,7 @@ if (crit != '_') { query['key'] = crit; - if (this._patterns_allowed) + if (this._submit_allowed) { query['operator'] = this.getWidget('operator').get('value'); if (this._pattern_is_list) @@ -195,13 +216,13 @@ // suitable for a given key _operators_query: function() { + if (this._operators_pending) + { + //alert("OPERATORS already pending!"); + } + this._set_query_pending('operator',true); try { - // While the query is underway I can always see the 'invalid' indicator - // at the 'operator' ComboBox. So I try to set the ComboBox to a - // neutral value. - this.getWidget('operator').set('value',null); - var value = this.getWidget('key').get('value'); return umc.tools.umcpCommand('pkgdb/operators',{ @@ -222,13 +243,13 @@ // key and operator. (pageKey is added silently) _proposals_query: function() { + if (this._proposals_pending) + { + //alert("PROPOSALS already pending!"); + } + this._set_query_pending('proposal',true); try { - // While the query is underway I can always see the 'invalid' indicator - // at the 'pattern_list' ComboBox. So I try to set the ComboBox to a - // neutral value. - this.getWidget('pattern_list').set('value',null); - var key = this.getWidget('key').get('value'); return umc.tools.umcpCommand('pkgdb/proposals',{ @@ -258,30 +279,51 @@ // entirely. // _handle_operators: function(values) { + + var p_label = this._("Pattern"); + var o_show = false; + var p_show = true; if (dojo.isArray(values)) { if (values.length) { this._set_selection_to_first_element('operator'); - this._allow_patterns(true); + this._pattern_needed = true; + o_show = true; } else { - this._allow_patterns(false); + this._pattern_needed = false; + p_show = false; } } else { - this._set_single_operator(values); + this._pattern_needed = true; + p_label = values; } + + this.showWidget('operator',o_show); + + if (p_show) + { + this.showWidget('pattern_text',!this._pattern_is_list); + this.showWidget('pattern_list',this._pattern_is_list); + } + else + { + this.showWidget('pattern_text',false); + this.showWidget('pattern_list',false); + } + + this.getWidget('pattern_text').set('label',p_label); + this.getWidget('pattern_list').set('label',p_label); }, // handles the result (and especially: the result type) of the // proposals returned by the 'pkgdb/proposals' query _handle_proposals: function(values) { - - if (! this._patterns_allowed) { return; } // nothing to do here. - + var is_single = dojo.isString(values); this._pattern_is_list = !is_single; // remember for later. if (is_single) @@ -292,49 +334,38 @@ { this._set_selection_to_first_element('pattern_list'); } - // values are set. now show/hide appropriately. - this.showWidget('pattern_text',is_single); - this.showWidget('pattern_list',!is_single); + // values are set. now show/hide appropriately, but only if needed. + if (this._pattern_needed) + { + this.showWidget('pattern_text',is_single); + this.showWidget('pattern_list',!is_single); + } }, - // If we get a single operator we use it as a label for the - // text input. Here is the function that switches the corresponding - // status values. - _set_single_operator: function(operator) { + // sets state of 'this query is pending' in a boolean variable + // and in the 'disabled' state of the corresponding dialog element(s) + _set_query_pending: function(element,on) { - this._patterns_allowed = true; - this.showWidget('operator',false); - this._set_selection_to_first_element('operator'); - this.getWidget('operator').set('value',operator); - - this.getWidget('pattern_text').set('label',operator); - this.getWidget('pattern_list').set('label',operator); + var bv = '_' + element + 's_pending'; + this.set(bv,on); - this.showWidget('pattern_text',true); - this.showWidget('pattern_list',false); - }, +// To make it 100% safe against impatient users... but the downside is that +// the dialog elements would flicker on every selection change at the 'key' +// ComboBox... +// +// var ele = this.getWidget(element); +// if (ele) +// { +// // applies to 'key' and 'operator' ComboBox +// ele.set('disabled',on); +// } +// else +// { +// // applies to these two 'pattern' entry elements +// this.getWidget('pattern_text').set('disabled',on); +// this.getWidget('pattern_list').set('disabled',on); +// } - // switches exutability of this query on or off: shows or hides - // the 'operator' and 'pattern' elements. Additionally remembers - // the last given pattern so subsequent handlers know the state. - _allow_patterns: function(allow) { - this._patterns_allowed = allow; - - this.showWidget('operator',allow); - - // at least, if NOT allowed -> hide both 'pattern_*' widgets. - // showing one of them depending on allowed values must - // be deferred until we have the 'proposals' data back. - if (! allow) - { - this.showWidget('pattern_text',false); - this.showWidget('pattern_list',false); - } - else - { - this.getWidget('pattern_text').set('label',this._("Pattern")); - this.getWidget('pattern_list').set('label',this._("Pattern")); - } }, _set_selection_to_first_element: function(name) { @@ -351,27 +382,68 @@ // feature if the form doesn't honor it? onSubmit: function() { - var keys = this.getWidget('key'); - var ok = ((keys.get('value') != '_') && (keys.getAllItems().length > 1)); - if (ok) + // the 'onChange' handler of the textbox is not invoked until the focus + // has left the field... so we have to do a last check here in case + // the text changed. + this._handle_query_changes(); + + if (this._submit_allowed) { - // this widget must not be honored since it is invisible - var ignore = (this._pattern_is_list ? 'pattern_text' : 'pattern_list'); + this.onExecuteQuery(this.getQuery()); + } + }, + + // an internal callback for everything that changes a query, operator or pattern. + // should call the external callback 'onQueryChanged' only if something has + // changed. This maintains all internal variables that reflect the state of + // the current entry and the executability of the query. + _handle_query_changes: function(name,value) { + + // start with: allowed if none of our dynamicValues queries is pending + var allow = ! (this._keys_pending || this._operators_pending || this._proposals_pending); + + // only allow if the 'key' position is not '--- select one ---' + allow = allow && (this.getWidget('key').get('value')!='_'); + + // check validation for all elements that must be valid + if (allow) + { for (var w in this._widgets) { var widget = this._widgets[w]; - if (widget.name != ignore) + var n = widget['name']; + var toprocess = true; + if (n.substr(0,8) == 'pattern_') { + if (this._pattern_needed) + { + if ( ((this._pattern_is_list) && (n == 'pattern_text')) + || ((!this._pattern_is_list) && (n == 'pattern_list'))) + { + toprocess = false; + } + } + else + { + // no pattern required: pattern_text AND pattern_list should + // should be ignored + toprocess = false; + } + } + if (toprocess) + { if (! widget.isValid()) { - ok = false; + allow = false; } } } } - if (ok) + + if (allow != this._submit_allowed) { - this.onExecuteQuery(this.getQuery()); + this._submit_allowed = allow; + this.enableSearchButton(allow); } }, @@ -389,14 +461,10 @@ this.enableEntryElements(false); }, - // invoked whenever a substantial dialog element is being changed. The - // ancestor can listen here and clear (or switch invisible) the result - // grid. Args are the name of the field being changed, and the new value. - onQueryChanged: function(field,value) { - if (field == 'key') - { - this.enableSearchButton(value != '_'); - } + // the invoking Page or Module can listen here to know that the query is become + // ready or disabled. Internal function _handle_query_changes() maintains all + // state variables and calls this only if the state has changed. + onQueryChanged: function(query) { } }); \ No newline at end of file Index: umc/js/_pkgdb/Page.js =================================================================== --- umc/js/_pkgdb/Page.js (revision 4255) +++ umc/js/_pkgdb/Page.js (working copy) @@ -61,19 +61,6 @@ this._current_query = query; - // ----------------- DEBUG ---------------------- -// var txt = "Executing query:\n~~~~~~~~~~~~~~~\n\n"; -// var fl = ['key','operator','pattern']; -// for (var f in fl) -// { -// if (query[fl[f]]) -// { -// txt += " '" + query[fl[f]] + "'"; -// } -// } -// alert(txt); - // ---------------------------------------------- - try { umc.tools.umcpCommand('pkgdb/columns',{ @@ -131,10 +118,6 @@ actions: [], columns: columns, moduleStore: umc.store.getModuleStore(fields[0],'pkgdb') -// onFilterDone: dojo.hitch(this, function(success) { -// alert("Filter done!"); -// this._searchform.enableSearchButton(true); -// }) }); if (this._grid)