# HG changeset patch # User Sylvain Thénault # Date 1268406716 -3600 # Node ID c666d265fb95d44ba7c67c2acfb5cc2329ea2b7f # Parent e402e0b32075353fe7662640599dd3d7f5f4144c# Parent 5dec0d400d0833c40e69e6ed3a237c73864c08eb backport stable diff -r e402e0b32075 -r c666d265fb95 .hgtags --- a/.hgtags Fri Mar 12 15:05:33 2010 +0100 +++ b/.hgtags Fri Mar 12 16:11:56 2010 +0100 @@ -105,3 +105,5 @@ 4ae30c9ca11b1edad67d25b76fce672171d02023 cubicweb-version-3.6.1 b9cdfe3341d1228687515d9af8686971ad5e6f5c cubicweb-debian-version-3.6.1-1 0a16f07112b90fb61d2e905855fece77e5a7e39c cubicweb-debian-version-3.6.1-2 +bfebe3d14d5390492925fc294dfdafad890a7104 cubicweb-version-3.6.2 +f3b4bb9121a0e7ee5961310ff79e61c890948a77 cubicweb-debian-version-3.6.2-1 diff -r e402e0b32075 -r c666d265fb95 __pkginfo__.py --- a/__pkginfo__.py Fri Mar 12 15:05:33 2010 +0100 +++ b/__pkginfo__.py Fri Mar 12 16:11:56 2010 +0100 @@ -7,7 +7,7 @@ distname = "cubicweb" modname = "cubicweb" -numversion = (3, 6, 1) +numversion = (3, 6, 2) version = '.'.join(str(num) for num in numversion) license = 'LGPL' diff -r e402e0b32075 -r c666d265fb95 _gcdebug.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/_gcdebug.py Fri Mar 12 16:11:56 2010 +0100 @@ -0,0 +1,87 @@ + +import gc, types, weakref + +from cubicweb.schema import CubicWebRelationSchema, CubicWebEntitySchema + +listiterator = type(iter([])) + +IGNORE_CLASSES = ( + type, tuple, dict, list, set, frozenset, type(len), + weakref.ref, weakref.WeakKeyDictionary, + listiterator, + property, classmethod, + types.ModuleType, types.FunctionType, types.MethodType, + types.MemberDescriptorType, types.GetSetDescriptorType, + ) + +def _get_counted_class(obj, classes): + for cls in classes: + if isinstance(obj, cls): + return cls + raise AssertionError() + +def gc_info(countclasses, + ignoreclasses=IGNORE_CLASSES, + viewreferrersclasses=(), showobjs=False, maxlevel=1): + gc.collect() + gc.collect() + counters = {} + ocounters = {} + for obj in gc.get_objects(): + if isinstance(obj, countclasses): + cls = _get_counted_class(obj, countclasses) + try: + counters[cls.__name__] += 1 + except KeyError: + counters[cls.__name__] = 1 + elif not isinstance(obj, ignoreclasses): + try: + key = '%s.%s' % (obj.__class__.__module__, + obj.__class__.__name__) + except AttributeError: + key = str(obj) + try: + ocounters[key] += 1 + except KeyError: + ocounters[key] = 1 + if isinstance(obj, viewreferrersclasses): + print ' ', obj, referrers(obj, showobjs, maxlevel) + return counters, ocounters, gc.garbage + + +def referrers(obj, showobj=False, maxlevel=1): + objreferrers = _referrers(obj, maxlevel) + try: + return sorted(set((type(x), showobj and x or getattr(x, '__name__', '%#x' % id(x))) + for x in objreferrers)) + except TypeError: + s = set() + unhashable = [] + for x in objreferrers: + try: + s.add(x) + except TypeError: + unhashable.append(x) + return sorted(s) + unhashable + +def _referrers(obj, maxlevel, _seen=None, _level=0): + interesting = [] + if _seen is None: + _seen = set() + for x in gc.get_referrers(obj): + if id(x) in _seen: + continue + _seen.add(id(x)) + if isinstance(x, types.FrameType): + continue + if isinstance(x, (CubicWebRelationSchema, CubicWebEntitySchema)): + continue + if isinstance(x, (list, tuple, set, dict, listiterator)): + if _level >= maxlevel: + pass + #interesting.append(x) + else: + interesting += _referrers(x, maxlevel, _seen, _level+1) + else: + interesting.append(x) + return interesting diff -r e402e0b32075 -r c666d265fb95 cwconfig.py --- a/cwconfig.py Fri Mar 12 15:05:33 2010 +0100 +++ b/cwconfig.py Fri Mar 12 16:11:56 2010 +0100 @@ -217,7 +217,9 @@ if os.environ.get('APYCOT_ROOT'): mode = 'test' - if CWDEV: + # allow to test cubes within apycot using cubicweb not installed by + # apycot + if __file__.startswith(os.environ['APYCOT_ROOT']): CUBES_DIR = '%(APYCOT_ROOT)s/local/share/cubicweb/cubes/' % os.environ # create __init__ file file(join(CUBES_DIR, '__init__.py'), 'w').close() @@ -638,16 +640,18 @@ """base class for cubicweb server and web configurations""" INSTANCES_DATA_DIR = None - if CubicWebNoAppConfiguration.mode == 'test': + if os.environ.get('APYCOT_ROOT'): root = os.environ['APYCOT_ROOT'] REGISTRY_DIR = '%s/etc/cubicweb.d/' % root + if not exists(REGISTRY_DIR): + os.makedirs(REGISTRY_DIR) RUNTIME_DIR = tempfile.gettempdir() - if CWDEV: + # allow to test cubes within apycot using cubicweb not installed by + # apycot + if __file__.startswith(os.environ['APYCOT_ROOT']): MIGRATION_DIR = '%s/local/share/cubicweb/migration/' % root else: MIGRATION_DIR = '/usr/share/cubicweb/migration/' - if not exists(REGISTRY_DIR): - os.makedirs(REGISTRY_DIR) else: if CubicWebNoAppConfiguration.mode == 'user': REGISTRY_DIR = expanduser('~/etc/cubicweb.d/') diff -r e402e0b32075 -r c666d265fb95 debian/changelog --- a/debian/changelog Fri Mar 12 15:05:33 2010 +0100 +++ b/debian/changelog Fri Mar 12 16:11:56 2010 +0100 @@ -1,3 +1,18 @@ +cubicweb (3.6.2-2) unstable; urgency=low + + * remove postgresql-contrib from cubicweb dependency (using tsearch + which is included with postgres >= 8.3) + * add postgresql-client | mysql-client to cubicweb-server dependencies, necessary + for dump/restore of database + + -- + +cubicweb (3.6.2-1) unstable; urgency=low + + * new upstream release + + -- Sylvain Thénault Thu, 11 Mar 2010 19:49:04 +0100 + cubicweb (3.6.1-2) unstable; urgency=low * remove depends to python-elementtree (included in python>=2.5) diff -r e402e0b32075 -r c666d265fb95 debian/control --- a/debian/control Fri Mar 12 15:05:33 2010 +0100 +++ b/debian/control Fri Mar 12 16:11:56 2010 +0100 @@ -16,7 +16,7 @@ Architecture: all XB-Python-Version: ${python:Versions} Depends: ${python:Depends}, cubicweb-server (= ${source:Version}), cubicweb-twisted (= ${source:Version}) -XB-Recommends: (postgresql, postgresql-plpython, postgresql-contrib) | mysql | sqlite3 +XB-Recommends: (postgresql, postgresql-plpython) | mysql | sqlite3 Recommends: postgresql | mysql | sqlite3 Description: the complete CubicWeb framework CubicWeb is a semantic web application framework. @@ -33,7 +33,8 @@ Conflicts: cubicweb-multisources Replaces: cubicweb-multisources Provides: cubicweb-multisources -Depends: ${python:Depends}, cubicweb-common (= ${source:Version}), cubicweb-ctl (= ${source:Version}), python-logilab-database, python-psycopg2 | python-mysqldb | python-pysqlite2 +# postgresql/mysql -client packages for backup/restore of non local database +Depends: ${python:Depends}, cubicweb-common (= ${source:Version}), cubicweb-ctl (= ${source:Version}), python-logilab-database, (python-psycopg2, postgresql-client) | (python-mysqldb, mysql-client) | python-pysqlite2 Recommends: pyro, cubicweb-documentation (= ${source:Version}) Description: server part of the CubicWeb framework CubicWeb is a semantic web application framework. diff -r e402e0b32075 -r c666d265fb95 doc/book/en/admin/instance-config.rst --- a/doc/book/en/admin/instance-config.rst Fri Mar 12 15:05:33 2010 +0100 +++ b/doc/book/en/admin/instance-config.rst Fri Mar 12 16:11:56 2010 +0100 @@ -49,13 +49,13 @@ and `https://localhost/demo` and actually running on port 8080, it takes to the http::: - RewriteCond %(REQUEST_URI) ^/demo + RewriteCond %{REQUEST_URI} ^/demo RewriteRule ^/demo$ /demo/ RewriteRule ^/demo/(.*) http://127.0.0.1:8080/$1 [L,P] and for the https::: - RewriteCond %(REQUEST_URI) ^/ demo + RewriteCond %{REQUEST_URI} ^/ demo RewriteRule ^/demo$/demo/ RewriteRule ^/demo/(.*) http://127.0.0.1:8080/https/$1 [L,P] @@ -63,7 +63,7 @@ and we will file in the all-in-one.conf of the instance::: base-url = http://localhost/demo - https-url = `https://localhost/demo` + https-url = https://localhost/demo Setting up the web client ------------------------- diff -r e402e0b32075 -r c666d265fb95 i18n/en.po --- a/i18n/en.po Fri Mar 12 15:05:33 2010 +0100 +++ b/i18n/en.po Fri Mar 12 16:11:56 2010 +0100 @@ -369,6 +369,9 @@ msgid "From:" msgstr "" +msgid "Garbage collection information" +msgstr "" + msgid "Help" msgstr "" @@ -390,6 +393,12 @@ msgid "Interval_plural" msgstr "Intervals" +msgid "Looked up classes" +msgstr "" + +msgid "Most referenced classes" +msgstr "" + msgid "New BaseTransition" msgstr "XXX" @@ -648,6 +657,9 @@ msgid "UniqueConstraint" msgstr "unique constraint" +msgid "Unreachable objects" +msgstr "" + msgid "Update permissions" msgstr "" @@ -2717,6 +2729,9 @@ msgid "may" msgstr "" +msgid "memory leak debugging" +msgstr "" + msgid "milestone" msgstr "" diff -r e402e0b32075 -r c666d265fb95 i18n/es.po --- a/i18n/es.po Fri Mar 12 15:05:33 2010 +0100 +++ b/i18n/es.po Fri Mar 12 16:11:56 2010 +0100 @@ -377,6 +377,9 @@ msgid "From:" msgstr "De: " +msgid "Garbage collection information" +msgstr "" + msgid "Help" msgstr "" @@ -398,6 +401,12 @@ msgid "Interval_plural" msgstr "Duraciones" +msgid "Looked up classes" +msgstr "" + +msgid "Most referenced classes" +msgstr "" + msgid "New BaseTransition" msgstr "" @@ -656,6 +665,9 @@ msgid "UniqueConstraint" msgstr "" +msgid "Unreachable objects" +msgstr "" + msgid "Update permissions" msgstr "Autorización de modificar" @@ -2785,6 +2797,9 @@ msgid "may" msgstr "Mayo" +msgid "memory leak debugging" +msgstr "" + msgid "milestone" msgstr "Milestone" diff -r e402e0b32075 -r c666d265fb95 i18n/fr.po --- a/i18n/fr.po Fri Mar 12 15:05:33 2010 +0100 +++ b/i18n/fr.po Fri Mar 12 16:11:56 2010 +0100 @@ -376,6 +376,9 @@ msgid "From:" msgstr "De :" +msgid "Garbage collection information" +msgstr "Information sur le ramasse-miette" + msgid "Help" msgstr "Aide" @@ -397,6 +400,12 @@ msgid "Interval_plural" msgstr "Durées" +msgid "Looked up classes" +msgstr "Classes recherchées" + +msgid "Most referenced classes" +msgstr "Classes les plus référencées" + msgid "New BaseTransition" msgstr "XXX" @@ -655,6 +664,9 @@ msgid "UniqueConstraint" msgstr "contrainte d'unicité" +msgid "Unreachable objects" +msgstr "Objets inacessible" + msgid "Update permissions" msgstr "Permissions de modifier" @@ -2811,6 +2823,9 @@ msgid "may" msgstr "mai" +msgid "memory leak debugging" +msgstr "Déboguage des fuites de mémoire" + msgid "milestone" msgstr "jalon" diff -r e402e0b32075 -r c666d265fb95 req.py --- a/req.py Fri Mar 12 15:05:33 2010 +0100 +++ b/req.py Fri Mar 12 16:11:56 2010 +0100 @@ -7,9 +7,10 @@ """ __docformat__ = "restructuredtext en" +from urlparse import urlsplit, urlunsplit from urllib import quote as urlquote, unquote as urlunquote from datetime import time, datetime, timedelta -from cgi import parse_qsl +from cgi import parse_qs, parse_qsl from logilab.common.decorators import cached from logilab.common.deprecation import deprecated @@ -239,7 +240,7 @@ def build_url_params(self, **kwargs): """return encoded params to incorporate them in an URL""" args = [] - for param, values in kwargs.items(): + for param, values in kwargs.iteritems(): if not isinstance(values, (list, tuple)): values = (values,) for value in values: @@ -279,6 +280,25 @@ except UnicodeDecodeError: # might occurs on manually typed URLs yield unicode(key, 'iso-8859-1'), unicode(val, 'iso-8859-1') + + def rebuild_url(self, url, **newparams): + """return the given url with newparams inserted. If any new params + is already specified in the url, it's overriden by the new value + + newparams may only be mono-valued. + """ + if isinstance(url, unicode): + url = url.encode(self.encoding) + schema, netloc, path, query, fragment = urlsplit(url) + query = parse_qs(query) + # sort for testing predictability + for key, val in sorted(newparams.iteritems()): + query[key] = (self.url_quote(val),) + query = '&'.join(u'%s=%s' % (param, value) + for param, values in query.items() + for value in values) + return urlunsplit((schema, netloc, path, query, fragment)) + # bound user related methods ############################################### @cached diff -r e402e0b32075 -r c666d265fb95 server/migractions.py --- a/server/migractions.py Fri Mar 12 15:05:33 2010 +0100 +++ b/server/migractions.py Fri Mar 12 16:11:56 2010 +0100 @@ -147,9 +147,9 @@ try: for source in repo.sources: try: - source.backup(osp.join(tmpdir, source.uri)) - except Exception, exc: - print '-> error trying to backup %s [%s]' % (source.uri, exc) + source.backup(osp.join(tmpdir, source.uri, self.confirm)) + except Exception, ex: + print '-> error trying to backup %s [%s]' % (source.uri, ex) if not self.confirm('Continue anyway?', default='n'): raise SystemExit(1) else: @@ -189,7 +189,6 @@ bkup = tarfile.open(backupfile, 'r|gz') bkup.extractall(path=tmpdir) bkup.close() - self.config.open_connections_pools = False repo = self.repo_connect() for source in repo.sources: diff -r e402e0b32075 -r c666d265fb95 server/repository.py --- a/server/repository.py Fri Mar 12 15:05:33 2010 +0100 +++ b/server/repository.py Fri Mar 12 16:11:56 2010 +0100 @@ -360,10 +360,15 @@ def stats(self): # XXX restrict to managers session? import threading results = {} - for hits, misses, title in ( - (self.querier.cache_hit, self.querier.cache_miss, 'rqlt_st'), - (self.system_source.cache_hit, self.system_source.cache_miss, 'sql'), + querier = self.querier + source = self.system_source + for size, maxsize, hits, misses, title in ( + (len(querier._rql_cache), self.config['rql-cache-size'], + querier.cache_hit, querier.cache_miss, 'rqlt_st'), + (len(source._cache), self.config['rql-cache-size'], + source.cache_hit, source.cache_miss, 'sql'), ): + results['%s_cache_size' % title] = '%s / %s' % (size, maxsize) results['%s_cache_hit' % title] = hits results['%s_cache_miss' % title] = misses results['%s_cache_hit_percent' % title] = (hits * 100) / (hits + misses) diff -r e402e0b32075 -r c666d265fb95 server/serverctl.py diff -r e402e0b32075 -r c666d265fb95 server/session.py --- a/server/session.py Fri Mar 12 15:05:33 2010 +0100 +++ b/server/session.py Fri Mar 12 16:11:56 2010 +0100 @@ -741,6 +741,7 @@ self.error('thread %s still alive after 10 seconds, will close ' 'session anyway', thread) self.rollback() + del self._threaddata # transaction data/operations management ################################## diff -r e402e0b32075 -r c666d265fb95 server/sources/extlite.py --- a/server/sources/extlite.py Fri Mar 12 15:05:33 2010 +0100 +++ b/server/sources/extlite.py Fri Mar 12 16:11:56 2010 +0100 @@ -87,11 +87,11 @@ AbstractSource.__init__(self, repo, appschema, source_config, *args, **kwargs) - def backup(self, backupfile): + def backup(self, backupfile, confirm): """method called to create a backup of the source's data""" self.close_pool_connections() try: - self.sqladapter.backup_to_file(backupfile) + self.sqladapter.backup_to_file(backupfile, confirm) finally: self.open_pool_connections() diff -r e402e0b32075 -r c666d265fb95 server/sources/native.py --- a/server/sources/native.py Fri Mar 12 15:05:33 2010 +0100 +++ b/server/sources/native.py Fri Mar 12 16:11:56 2010 +0100 @@ -13,7 +13,7 @@ """ __docformat__ = "restructuredtext en" -from threading import Lock +from o_threading import Lock from datetime import datetime from base64 import b64decode, b64encode @@ -205,11 +205,11 @@ pool.pool_reset() self.repo._free_pool(pool) - def backup(self, backupfile): + def backup(self, backupfile, confirm): """method called to create a backup of the source's data""" self.close_pool_connections() try: - self.backup_to_file(backupfile) + self.backup_to_file(backupfile, confirm) finally: self.open_pool_connections() diff -r e402e0b32075 -r c666d265fb95 server/sqlutils.py --- a/server/sqlutils.py Fri Mar 12 15:05:33 2010 +0100 +++ b/server/sqlutils.py Fri Mar 12 16:11:56 2010 +0100 @@ -159,7 +159,7 @@ """open and return a connection to the database""" return self.dbhelper.get_connection() - def backup_to_file(self, backupfile): + def backup_to_file(self, backupfile, confirm): for cmd in self.dbhelper.backup_commands(backupfile, keepownership=False): if _run_command(cmd): diff -r e402e0b32075 -r c666d265fb95 test/unittest_req.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/unittest_req.py Fri Mar 12 16:11:56 2010 +0100 @@ -0,0 +1,16 @@ +from logilab.common.testlib import TestCase, unittest_main +from cubicweb.req import RequestSessionBase + +class RebuildURLTC(TestCase): + def test(self): + rebuild_url = RequestSessionBase(None).rebuild_url + self.assertEquals(rebuild_url('http://logilab.fr?__message=pouet', __message='hop'), + 'http://logilab.fr?__message=hop') + self.assertEquals(rebuild_url('http://logilab.fr', __message='hop'), + 'http://logilab.fr?__message=hop') + self.assertEquals(rebuild_url('http://logilab.fr?vid=index', __message='hop'), + 'http://logilab.fr?__message=hop&vid=index') + + +if __name__ == '__main__': + unittest_main() diff -r e402e0b32075 -r c666d265fb95 test/unittest_utils.py --- a/test/unittest_utils.py Fri Mar 12 15:05:33 2010 +0100 +++ b/test/unittest_utils.py Fri Mar 12 16:11:56 2010 +0100 @@ -59,7 +59,7 @@ l.extend(extension) yield self.assertEquals, l, expected -class JSONEncoerTests(TestCase): +class JSONEncoderTC(TestCase): def setUp(self): if simplejson is None: self.skip('simplejson not available') @@ -81,5 +81,6 @@ def test_encoding_unknown_stuff(self): self.assertEquals(self.encode(TestCase), 'null') + if __name__ == '__main__': unittest_main() diff -r e402e0b32075 -r c666d265fb95 utils.py --- a/utils.py Fri Mar 12 15:05:33 2010 +0100 +++ b/utils.py Fri Mar 12 16:11:56 2010 +0100 @@ -160,7 +160,6 @@ warn('[3.7] specifying jsoncall is not needed anymore', DeprecationWarning, stacklevel=2) self.add_post_inline_script(u"""jQuery(CubicWeb).one('server-response', function(event) { -%s });""" % jscode) @@ -182,10 +181,10 @@ if (cssfile, media) not in self.cssfiles: self.cssfiles.append( (cssfile, media) ) - def add_ie_css(self, cssfile, media='all'): + def add_ie_css(self, cssfile, media='all', iespec=u'[if lt IE 8]'): """registers some IE specific CSS""" - if (cssfile, media) not in self.ie_cssfiles: - self.ie_cssfiles.append( (cssfile, media) ) + if (cssfile, media, iespec) not in self.ie_cssfiles: + self.ie_cssfiles.append( (cssfile, media, iespec) ) def add_unload_pagedata(self): """registers onunload callback to clean page data on server""" @@ -215,8 +214,8 @@ (media, xml_escape(cssfile))) # 3/ ie css if necessary if self.ie_cssfiles: - w(u' \n') diff -r e402e0b32075 -r c666d265fb95 web/data/jquery.autocomplete.js --- a/web/data/jquery.autocomplete.js Fri Mar 12 15:05:33 2010 +0100 +++ b/web/data/jquery.autocomplete.js Fri Mar 12 16:11:56 2010 +0100 @@ -1,15 +1,13 @@ /* - * Autocomplete - jQuery plugin 1.0.2 + * jQuery Autocomplete plugin 1.1 * - * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer + * Copyright (c) 2009 Jörn Zaefferer * * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html * - * Revision: $Id: jquery.autocomplete.js 5747 2008-06-25 18:30:55Z joern.zaefferer $ - * - */;(function($){$.fn.extend({autocomplete:function(urlOrData,options){var isUrl=typeof urlOrData=="string";options=$.extend({},$.Autocompleter.defaults,{url:isUrl?urlOrData:null,data:isUrl?null:urlOrData,delay:isUrl?$.Autocompleter.defaults.delay:10,max:options&&!options.scroll?10:150},options);options.highlight=options.highlight||function(value){return value;};options.formatMatch=options.formatMatch||options.formatItem;return this.each(function(){new $.Autocompleter(this,options);});},result:function(handler){return this.bind("result",handler);},search:function(handler){return this.trigger("search",[handler]);},flushCache:function(){return this.trigger("flushCache");},setOptions:function(options){return this.trigger("setOptions",[options]);},unautocomplete:function(){return this.trigger("unautocomplete");}});$.Autocompleter=function(input,options){var KEY={UP:38,DOWN:40,DEL:46,TAB:9,RETURN:13,ESC:27,COMMA:188,PAGEUP:33,PAGEDOWN:34,BACKSPACE:8};var $input=$(input).attr("autocomplete","off").addClass(options.inputClass);var timeout;var previousValue="";var cache=$.Autocompleter.Cache(options);var hasFocus=0;var lastKeyPressCode;var config={mouseDownOnSelect:false};var select=$.Autocompleter.Select(options,input,selectCurrent,config);var blockSubmit;$.browser.opera&&$(input.form).bind("submit.autocomplete",function(){if(blockSubmit){blockSubmit=false;return false;}});$input.bind(($.browser.opera?"keypress":"keydown")+".autocomplete",function(event){lastKeyPressCode=event.keyCode;switch(event.keyCode){case KEY.UP:event.preventDefault();if(select.visible()){select.prev();}else{onChange(0,true);}break;case KEY.DOWN:event.preventDefault();if(select.visible()){select.next();}else{onChange(0,true);}break;case KEY.PAGEUP:event.preventDefault();if(select.visible()){select.pageUp();}else{onChange(0,true);}break;case KEY.PAGEDOWN:event.preventDefault();if(select.visible()){select.pageDown();}else{onChange(0,true);}break;case options.multiple&&$.trim(options.multipleSeparator)==","&&KEY.COMMA:case KEY.TAB:case KEY.RETURN:if(selectCurrent()){event.preventDefault();blockSubmit=true;return false;}break;case KEY.ESC:select.hide();break;default:clearTimeout(timeout);timeout=setTimeout(onChange,options.delay);break;}}).focus(function(){hasFocus++;}).blur(function(){hasFocus=0;if(!config.mouseDownOnSelect){hideResults();}}).click(function(){if(hasFocus++>1&&!select.visible()){onChange(0,true);}}).bind("search",function(){var fn=(arguments.length>1)?arguments[1]:null;function findValueCallback(q,data){var result;if(data&&data.length){for(var i=0;i1){v=words.slice(0,words.length-1).join(options.multipleSeparator)+options.multipleSeparator+v;}v+=options.multipleSeparator;}$input.val(v);hideResultsNow();$input.trigger("result",[selected.data,selected.value]);return true;}function onChange(crap,skipPrevCheck){if(lastKeyPressCode==KEY.DEL){select.hide();return;}var currentValue=$input.val();if(!skipPrevCheck&¤tValue==previousValue)return;previousValue=currentValue;currentValue=lastWord(currentValue);if(currentValue.length>=options.minChars){$input.addClass(options.loadingClass);if(!options.matchCase)currentValue=currentValue.toLowerCase();request(currentValue,receiveData,hideResultsNow);}else{stopLoading();select.hide();}};function trimWords(value){if(!value){return[""];}var words=value.split(options.multipleSeparator);var result=[];$.each(words,function(i,value){if($.trim(value))result[i]=$.trim(value);});return result;}function lastWord(value){if(!options.multiple)return value;var words=trimWords(value);return words[words.length-1];}function autoFill(q,sValue){if(options.autoFill&&(lastWord($input.val()).toLowerCase()==q.toLowerCase())&&lastKeyPressCode!=KEY.BACKSPACE){$input.val($input.val()+sValue.substring(lastWord(previousValue).length));$.Autocompleter.Selection(input,previousValue.length,previousValue.length+sValue.length);}};function hideResults(){clearTimeout(timeout);timeout=setTimeout(hideResultsNow,200);};function hideResultsNow(){var wasVisible=select.visible();select.hide();clearTimeout(timeout);stopLoading();if(options.mustMatch){$input.search(function(result){if(!result){if(options.multiple){var words=trimWords($input.val()).slice(0,-1);$input.val(words.join(options.multipleSeparator)+(words.length?options.multipleSeparator:""));}else -$input.val("");}});}if(wasVisible)$.Autocompleter.Selection(input,input.value.length,input.value.length);};function receiveData(q,data){if(data&&data.length&&hasFocus){stopLoading();select.display(data,q);autoFill(q,data[0].value);select.show();}else{hideResultsNow();}};function request(term,success,failure){if(!options.matchCase)term=term.toLowerCase();var data=cache.load(term);if(data&&data.length){success(term,data);}else if((typeof options.url=="string")&&(options.url.length>0)){var extraParams={timestamp:+new Date()};$.each(options.extraParams,function(key,param){extraParams[key]=typeof param=="function"?param():param;});$.ajax({mode:"abort",port:"autocomplete"+input.name,dataType:options.dataType,url:options.url,data:$.extend({q:lastWord(term),limit:options.max},extraParams),success:function(data){var parsed=options.parse&&options.parse(data)||parse(data);cache.add(term,parsed);success(term,parsed);}});}else{select.emptyList();failure(term);}};function parse(data){var parsed=[];var rows=data.split("\n");for(var i=0;i]*)("+term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi,"\\$1")+")(?![^<>]*>)(?![^&;]+;)","gi"),"$1");},scroll:true,scrollHeight:180};$.Autocompleter.Cache=function(options){var data={};var length=0;function matchSubset(s,sub){if(!options.matchCase)s=s.toLowerCase();var i=s.indexOf(sub);if(i==-1)return false;return i==0||options.matchContains;};function add(q,value){if(length>options.cacheLength){flush();}if(!data[q]){length++;}data[q]=value;}function populate(){if(!options.data)return false;var stMatchSets={},nullData=0;if(!options.url)options.cacheLength=1;stMatchSets[""]=[];for(var i=0,ol=options.data.length;i0){var c=data[k];$.each(c,function(i,x){if(matchSubset(x.value,q)){csub.push(x);}});}}return csub;}else + * Revision: $Id: jquery.autocomplete.js 15 2009-08-22 10:30:27Z joern.zaefferer $ + */;(function($){$.fn.extend({autocomplete:function(urlOrData,options){var isUrl=typeof urlOrData=="string";options=$.extend({},$.Autocompleter.defaults,{url:isUrl?urlOrData:null,data:isUrl?null:urlOrData,delay:isUrl?$.Autocompleter.defaults.delay:10,max:options&&!options.scroll?10:150},options);options.highlight=options.highlight||function(value){return value;};options.formatMatch=options.formatMatch||options.formatItem;return this.each(function(){new $.Autocompleter(this,options);});},result:function(handler){return this.bind("result",handler);},search:function(handler){return this.trigger("search",[handler]);},flushCache:function(){return this.trigger("flushCache");},setOptions:function(options){return this.trigger("setOptions",[options]);},unautocomplete:function(){return this.trigger("unautocomplete");}});$.Autocompleter=function(input,options){var KEY={UP:38,DOWN:40,DEL:46,TAB:9,RETURN:13,ESC:27,COMMA:188,PAGEUP:33,PAGEDOWN:34,BACKSPACE:8};var $input=$(input).attr("autocomplete","off").addClass(options.inputClass);var timeout;var previousValue="";var cache=$.Autocompleter.Cache(options);var hasFocus=0;var lastKeyPressCode;var config={mouseDownOnSelect:false};var select=$.Autocompleter.Select(options,input,selectCurrent,config);var blockSubmit;$.browser.opera&&$(input.form).bind("submit.autocomplete",function(){if(blockSubmit){blockSubmit=false;return false;}});$input.bind(($.browser.opera?"keypress":"keydown")+".autocomplete",function(event){hasFocus=1;lastKeyPressCode=event.keyCode;switch(event.keyCode){case KEY.UP:event.preventDefault();if(select.visible()){select.prev();}else{onChange(0,true);}break;case KEY.DOWN:event.preventDefault();if(select.visible()){select.next();}else{onChange(0,true);}break;case KEY.PAGEUP:event.preventDefault();if(select.visible()){select.pageUp();}else{onChange(0,true);}break;case KEY.PAGEDOWN:event.preventDefault();if(select.visible()){select.pageDown();}else{onChange(0,true);}break;case options.multiple&&$.trim(options.multipleSeparator)==","&&KEY.COMMA:case KEY.TAB:case KEY.RETURN:if(selectCurrent()){event.preventDefault();blockSubmit=true;return false;}break;case KEY.ESC:select.hide();break;default:clearTimeout(timeout);timeout=setTimeout(onChange,options.delay);break;}}).focus(function(){hasFocus++;}).blur(function(){hasFocus=0;if(!config.mouseDownOnSelect){hideResults();}}).click(function(){if(hasFocus++>1&&!select.visible()){onChange(0,true);}}).bind("search",function(){var fn=(arguments.length>1)?arguments[1]:null;function findValueCallback(q,data){var result;if(data&&data.length){for(var i=0;i1){var seperator=options.multipleSeparator.length;var cursorAt=$(input).selection().start;var wordAt,progress=0;$.each(words,function(i,word){progress+=word.length;if(cursorAt<=progress){wordAt=i;return false;}progress+=seperator;});words[wordAt]=v;v=words.join(options.multipleSeparator);}v+=options.multipleSeparator;}$input.val(v);hideResultsNow();$input.trigger("result",[selected.data,selected.value]);return true;}function onChange(crap,skipPrevCheck){if(lastKeyPressCode==KEY.DEL){select.hide();return;}var currentValue=$input.val();if(!skipPrevCheck&¤tValue==previousValue)return;previousValue=currentValue;currentValue=lastWord(currentValue);if(currentValue.length>=options.minChars){$input.addClass(options.loadingClass);if(!options.matchCase)currentValue=currentValue.toLowerCase();request(currentValue,receiveData,hideResultsNow);}else{stopLoading();select.hide();}};function trimWords(value){if(!value)return[""];if(!options.multiple)return[$.trim(value)];return $.map(value.split(options.multipleSeparator),function(word){return $.trim(value).length?$.trim(word):null;});}function lastWord(value){if(!options.multiple)return value;var words=trimWords(value);if(words.length==1)return words[0];var cursorAt=$(input).selection().start;if(cursorAt==value.length){words=trimWords(value)}else{words=trimWords(value.replace(value.substring(cursorAt),""));}return words[words.length-1];}function autoFill(q,sValue){if(options.autoFill&&(lastWord($input.val()).toLowerCase()==q.toLowerCase())&&lastKeyPressCode!=KEY.BACKSPACE){$input.val($input.val()+sValue.substring(lastWord(previousValue).length));$(input).selection(previousValue.length,previousValue.length+sValue.length);}};function hideResults(){clearTimeout(timeout);timeout=setTimeout(hideResultsNow,200);};function hideResultsNow(){var wasVisible=select.visible();select.hide();clearTimeout(timeout);stopLoading();if(options.mustMatch){$input.search(function(result){if(!result){if(options.multiple){var words=trimWords($input.val()).slice(0,-1);$input.val(words.join(options.multipleSeparator)+(words.length?options.multipleSeparator:""));}else{$input.val("");$input.trigger("result",null);}}});}};function receiveData(q,data){if(data&&data.length&&hasFocus){stopLoading();select.display(data,q);autoFill(q,data[0].value);select.show();}else{hideResultsNow();}};function request(term,success,failure){if(!options.matchCase)term=term.toLowerCase();var data=cache.load(term);if(data&&data.length){success(term,data);}else if((typeof options.url=="string")&&(options.url.length>0)){var extraParams={timestamp:+new Date()};$.each(options.extraParams,function(key,param){extraParams[key]=typeof param=="function"?param():param;});$.ajax({mode:"abort",port:"autocomplete"+input.name,dataType:options.dataType,url:options.url,data:$.extend({q:lastWord(term),limit:options.max},extraParams),success:function(data){var parsed=options.parse&&options.parse(data)||parse(data);cache.add(term,parsed);success(term,parsed);}});}else{select.emptyList();failure(term);}};function parse(data){var parsed=[];var rows=data.split("\n");for(var i=0;i]*)("+term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi,"\\$1")+")(?![^<>]*>)(?![^&;]+;)","gi"),"$1");},scroll:true,scrollHeight:180};$.Autocompleter.Cache=function(options){var data={};var length=0;function matchSubset(s,sub){if(!options.matchCase)s=s.toLowerCase();var i=s.indexOf(sub);if(options.matchContains=="word"){i=s.toLowerCase().search("\\b"+sub.toLowerCase());}if(i==-1)return false;return i==0||options.matchContains;};function add(q,value){if(length>options.cacheLength){flush();}if(!data[q]){length++;}data[q]=value;}function populate(){if(!options.data)return false;var stMatchSets={},nullData=0;if(!options.url)options.cacheLength=1;stMatchSets[""]=[];for(var i=0,ol=options.data.length;i0){var c=data[k];$.each(c,function(i,x){if(matchSubset(x.value,q)){csub.push(x);}});}}return csub;}else if(data[q]){return data[q];}else -if(options.matchSubset){for(var i=q.length-1;i>=options.minChars;i--){var c=data[q.substr(0,i)];if(c){var csub=[];$.each(c,function(i,x){if(matchSubset(x.value,q)){csub[csub.length]=x;}});return csub;}}}return null;}};};$.Autocompleter.Select=function(options,input,select,config){var CLASSES={ACTIVE:"ac_over"};var listItems,active=-1,data,term="",needsInit=true,element,list;function init(){if(!needsInit)return;element=$("
").hide().addClass(options.resultsClass).css("position","absolute").appendTo(document.body);list=$("
    ").appendTo(element).mouseover(function(event){if(target(event).nodeName&&target(event).nodeName.toUpperCase()=='LI'){active=$("li",list).removeClass(CLASSES.ACTIVE).index(target(event));$(target(event)).addClass(CLASSES.ACTIVE);}}).click(function(event){$(target(event)).addClass(CLASSES.ACTIVE);select();input.focus();return false;}).mousedown(function(){config.mouseDownOnSelect=true;}).mouseup(function(){config.mouseDownOnSelect=false;});if(options.width>0)element.css("width",options.width);needsInit=false;}function target(event){var element=event.target;while(element&&element.tagName!="LI")element=element.parentNode;if(!element)return[];return element;}function moveSelect(step){listItems.slice(active,active+1).removeClass(CLASSES.ACTIVE);movePosition(step);var activeItem=listItems.slice(active,active+1).addClass(CLASSES.ACTIVE);if(options.scroll){var offset=0;listItems.slice(0,active).each(function(){offset+=this.offsetHeight;});if((offset+activeItem[0].offsetHeight-list.scrollTop())>list[0].clientHeight){list.scrollTop(offset+activeItem[0].offsetHeight-list.innerHeight());}else if(offset=listItems.size()){active=0;}}function limitNumberOfItems(available){return options.max&&options.max").html(options.highlight(formatted,term)).addClass(i%2==0?"ac_even":"ac_odd").appendTo(list)[0];$.data(li,"ac_data",data[i]);}listItems=list.find("li");if(options.selectFirst){listItems.slice(0,1).addClass(CLASSES.ACTIVE);active=0;}if($.fn.bgiframe)list.bgiframe();}return{display:function(d,q){init();data=d;term=q;fillList();},next:function(){moveSelect(1);},prev:function(){moveSelect(-1);},pageUp:function(){if(active!=0&&active-8<0){moveSelect(-active);}else{moveSelect(-8);}},pageDown:function(){if(active!=listItems.size()-1&&active+8>listItems.size()){moveSelect(listItems.size()-1-active);}else{moveSelect(8);}},hide:function(){element&&element.hide();listItems&&listItems.removeClass(CLASSES.ACTIVE);active=-1;},visible:function(){return element&&element.is(":visible");},current:function(){return this.visible()&&(listItems.filter("."+CLASSES.ACTIVE)[0]||options.selectFirst&&listItems[0]);},show:function(){var offset=$(input).offset();element.css({width:typeof options.width=="string"||options.width>0?options.width:$(input).width(),top:offset.top+input.offsetHeight,left:offset.left}).show();if(options.scroll){list.scrollTop(0);list.css({maxHeight:options.scrollHeight,overflow:'auto'});if($.browser.msie&&typeof document.body.style.maxHeight==="undefined"){var listHeight=0;listItems.each(function(){listHeight+=this.offsetHeight;});var scrollbarsVisible=listHeight>options.scrollHeight;list.css('height',scrollbarsVisible?options.scrollHeight:listHeight);if(!scrollbarsVisible){listItems.width(list.width()-parseInt(listItems.css("padding-left"))-parseInt(listItems.css("padding-right")));}}}},selected:function(){var selected=listItems&&listItems.filter("."+CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);return selected&&selected.length&&$.data(selected[0],"ac_data");},emptyList:function(){list&&list.empty();},unbind:function(){element&&element.remove();}};};$.Autocompleter.Selection=function(field,start,end){if(field.createTextRange){var selRange=field.createTextRange();selRange.collapse(true);selRange.moveStart("character",start);selRange.moveEnd("character",end);selRange.select();}else if(field.setSelectionRange){field.setSelectionRange(start,end);}else{if(field.selectionStart){field.selectionStart=start;field.selectionEnd=end;}}field.focus();};})(jQuery); \ No newline at end of file +if(options.matchSubset){for(var i=q.length-1;i>=options.minChars;i--){var c=data[q.substr(0,i)];if(c){var csub=[];$.each(c,function(i,x){if(matchSubset(x.value,q)){csub[csub.length]=x;}});return csub;}}}return null;}};};$.Autocompleter.Select=function(options,input,select,config){var CLASSES={ACTIVE:"ac_over"};var listItems,active=-1,data,term="",needsInit=true,element,list;function init(){if(!needsInit)return;element=$("
    ").hide().addClass(options.resultsClass).css("position","absolute").appendTo(document.body);list=$("
      ").appendTo(element).mouseover(function(event){if(target(event).nodeName&&target(event).nodeName.toUpperCase()=='LI'){active=$("li",list).removeClass(CLASSES.ACTIVE).index(target(event));$(target(event)).addClass(CLASSES.ACTIVE);}}).click(function(event){$(target(event)).addClass(CLASSES.ACTIVE);select();input.focus();return false;}).mousedown(function(){config.mouseDownOnSelect=true;}).mouseup(function(){config.mouseDownOnSelect=false;});if(options.width>0)element.css("width",options.width);needsInit=false;}function target(event){var element=event.target;while(element&&element.tagName.toUpperCase()!="LI")element=element.parentNode;if(!element)return[];return element;}function moveSelect(step){listItems.slice(active,active+1).removeClass(CLASSES.ACTIVE);movePosition(step);var activeItem=listItems.slice(active,active+1).addClass(CLASSES.ACTIVE);if(options.scroll){var offset=0;listItems.slice(0,active).each(function(){offset+=this.offsetHeight;});if((offset+activeItem[0].offsetHeight-list.scrollTop())>list[0].clientHeight){list.scrollTop(offset+activeItem[0].offsetHeight-list.innerHeight());}else if(offset=listItems.size()){active=0;}}function limitNumberOfItems(available){return options.max&&options.max").html(options.highlight(formatted,term)).addClass(i%2==0?"ac_even":"ac_odd").appendTo(list)[0];$.data(li,"ac_data",data[i]);}listItems=list.find("li");if(options.selectFirst){listItems.slice(0,1).addClass(CLASSES.ACTIVE);active=0;}if($.fn.bgiframe)list.bgiframe();}return{display:function(d,q){init();data=d;term=q;fillList();},next:function(){moveSelect(1);},prev:function(){moveSelect(-1);},pageUp:function(){if(active!=0&&active-8<0){moveSelect(-active);}else{moveSelect(-8);}},pageDown:function(){if(active!=listItems.size()-1&&active+8>listItems.size()){moveSelect(listItems.size()-1-active);}else{moveSelect(8);}},hide:function(){element&&element.hide();listItems&&listItems.removeClass(CLASSES.ACTIVE);active=-1;},visible:function(){return element&&element.is(":visible");},current:function(){return this.visible()&&(listItems.filter("."+CLASSES.ACTIVE)[0]||options.selectFirst&&listItems[0]);},show:function(){var offset=$(input).offset();element.css({width:typeof options.width=="string"||options.width>0?options.width:$(input).width(),top:offset.top+input.offsetHeight,left:offset.left}).show();if(options.scroll){list.scrollTop(0);list.css({maxHeight:options.scrollHeight,overflow:'auto'});if($.browser.msie&&typeof document.body.style.maxHeight==="undefined"){var listHeight=0;listItems.each(function(){listHeight+=this.offsetHeight;});var scrollbarsVisible=listHeight>options.scrollHeight;list.css('height',scrollbarsVisible?options.scrollHeight:listHeight);if(!scrollbarsVisible){listItems.width(list.width()-parseInt(listItems.css("padding-left"))-parseInt(listItems.css("padding-right")));}}}},selected:function(){var selected=listItems&&listItems.filter("."+CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);return selected&&selected.length&&$.data(selected[0],"ac_data");},emptyList:function(){list&&list.empty();},unbind:function(){element&&element.remove();}};};$.fn.selection=function(start,end){if(start!==undefined){return this.each(function(){if(this.createTextRange){var selRange=this.createTextRange();if(end===undefined||start==end){selRange.move("character",start);selRange.select();}else{selRange.collapse(true);selRange.moveStart("character",start);selRange.moveEnd("character",end);selRange.select();}}else if(this.setSelectionRange){this.setSelectionRange(start,end);}else if(this.selectionStart){this.selectionStart=start;this.selectionEnd=end;}});}var field=this[0];if(field.createTextRange){var range=document.selection.createRange(),orig=field.value,teststring="<->",textLength=range.text.length;range.text=teststring;var caretAt=field.value.indexOf(teststring);field.value=orig;this.selection(caretAt,caretAt+textLength);return{start:caretAt,end:caretAt+textLength}}else if(field.selectionStart!==undefined){return{start:field.selectionStart,end:field.selectionEnd}}};})(jQuery); \ No newline at end of file diff -r e402e0b32075 -r c666d265fb95 web/formfields.py --- a/web/formfields.py Fri Mar 12 15:05:33 2010 +0100 +++ b/web/formfields.py Fri Mar 12 16:11:56 2010 +0100 @@ -14,7 +14,6 @@ from logilab.mtconverter import xml_escape from logilab.common.date import ustrftime -from logilab.common.decorators import cached from yams.schema import KNOWN_METAATTRIBUTES from yams.constraints import (SizeConstraint, StaticVocabularyConstraint, @@ -189,19 +188,28 @@ """return the widget instance associated to this field""" return self.widget - # cached is necessary else we get some pb on entity creation : entity.eid is - # modified from creation mark (eg 'X') to its actual eid (eg 123), and then - # `field.input_name()` won't return the right key anymore if not cached - # (first call to input_name done *before* eventual eid affectation). - @cached def input_name(self, form, suffix=None): """return 'qualified name' for this field""" - name = self.role_name() - if suffix is not None: - name += suffix - if self.eidparam: - return eid_param(name, form.edited_entity.eid) - return name + # caching is necessary else we get some pb on entity creation : + # entity.eid is modified from creation mark (eg 'X') to its actual eid + # (eg 123), and then `field.input_name()` won't return the right key + # anymore if not cached (first call to input_name done *before* eventual + # eid affectation). + # + # note that you should NOT use @cached else it will create a memory leak + # on persistent fields (eg created once for all on a form class) because + # of the 'form' appobject argument: the cache will keep growing as new + # form are created... + try: + return form.formvalues[(self, 'input_name', suffix)] + except KeyError: + name = self.role_name() + if suffix is not None: + name += suffix + if self.eidparam: + name = eid_param(name, form.edited_entity.eid) + form.formvalues[(self, 'input_name', suffix)] = name + return name def role_name(self): """return - if role is specified, else field.name""" diff -r e402e0b32075 -r c666d265fb95 web/formwidgets.py --- a/web/formwidgets.py Fri Mar 12 15:05:33 2010 +0100 +++ b/web/formwidgets.py Fri Mar 12 16:11:56 2010 +0100 @@ -456,16 +456,17 @@ needs_js = ('jquery.timePicker.js',) needs_css = ('jquery.timepicker.css',) - def __init__(self, timestr=None, timesteps=30, **kwargs): + def __init__(self, timestr=None, timesteps=30, separator=u':', **kwargs): super(JQueryTimePicker, self).__init__(**kwargs) self.timestr = timestr self.timesteps = timesteps + self.separator = separator def _render(self, form, field, renderer): req = form._cw domid = field.dom_id(form, self.suffix) - req.add_onload(u'jqNode("%s").timePicker({selectedTime: "%s", step: %s})' % ( - domid, self.timestr, self.timesteps)) + req.add_onload(u'jqNode("%s").timePicker({selectedTime: "%s", step: %s, separator: "%s"})' % ( + domid, self.timestr, self.timesteps, self.separator)) if self.timestr is None: value = self.values(form, field)[0] else: diff -r e402e0b32075 -r c666d265fb95 web/request.py --- a/web/request.py Fri Mar 12 15:05:33 2010 +0100 +++ b/web/request.py Fri Mar 12 16:11:56 2010 +0100 @@ -311,7 +311,11 @@ if breadcrumbs is None: breadcrumbs = SizeConstrainedList(10) self.set_session_data('breadcrumbs', breadcrumbs) - breadcrumbs.append(self.url()) + breadcrumbs.append(self.url()) + else: + url = self.url() + if breadcrumbs[-1] != url: + breadcrumbs.append(url) def last_visited_page(self): breadcrumbs = self.get_session_data('breadcrumbs', None) @@ -516,26 +520,33 @@ jsfile = self.datadir_url + jsfile self.html_headers.add_js(jsfile) - def add_css(self, cssfiles, media=u'all', localfile=True, ieonly=False): + def add_css(self, cssfiles, media=u'all', localfile=True, ieonly=False, + iespec=u'[if lt IE 8]'): """specify a CSS file to include in the HTML headers :param cssfiles: a CSS filename or a list of CSS filenames :param media: the CSS's media if necessary :param localfile: if True, the default data dir prefix is added to the CSS filename + :param ieonly: True if this css is specific to IE + :param iespec: conditional expression that will be used around + the css inclusion. cf: + http://msdn.microsoft.com/en-us/library/ms537512(VS.85).aspx """ if isinstance(cssfiles, basestring): cssfiles = (cssfiles,) if ieonly: if self.ie_browser(): + extraargs = [iespec] add_css = self.html_headers.add_ie_css else: return # no need to do anything on non IE browsers else: + extraargs = [] add_css = self.html_headers.add_css for cssfile in cssfiles: if localfile: cssfile = self.datadir_url + cssfile - add_css(cssfile, media) + add_css(cssfile, media, *extraargs) def build_ajax_replace_url(self, nodeid, rql, vid, replacemode='replace', **extraparams): diff -r e402e0b32075 -r c666d265fb95 web/views/authentication.py --- a/web/views/authentication.py Fri Mar 12 15:05:33 2010 +0100 +++ b/web/views/authentication.py Fri Mar 12 16:11:56 2010 +0100 @@ -114,8 +114,11 @@ login, authinfo = retreiver.authentication_information(req) except NoAuthInfo: continue - cnx = self._authenticate(req, login, authinfo) - break + try: + cnx = self._authenticate(req, login, authinfo) + break + except ExplicitLogin: + continue # the next one may succeed else: raise ExplicitLogin() for retreiver_ in self.authinforetreivers: @@ -124,7 +127,6 @@ def _authenticate(self, req, login, authinfo): # remove possibly cached cursor coming from closed connection - clear_cache(req, 'cursor') cnxprops = ConnectionProperties(self.vreg.config.repo_method, close=False, log=self.log_queries) try: diff -r e402e0b32075 -r c666d265fb95 web/views/debug.py --- a/web/views/debug.py Fri Mar 12 15:05:33 2010 +0100 +++ b/web/views/debug.py Fri Mar 12 16:11:56 2010 +0100 @@ -27,13 +27,13 @@ class ProcessInformationView(StartupView): + """display various web server /repository information""" __regid__ = 'info' __select__ = none_rset() & match_user_groups('managers') title = _('server information') def call(self, **kwargs): - """display server information""" req = self._cw dtformat = req.property_value('ui.datetime-format') _ = req._ @@ -111,24 +111,59 @@ class RegistryView(StartupView): + """display vregistry content""" __regid__ = 'registry' __select__ = StartupView.__select__ & match_user_groups('managers') title = _('registry') def call(self, **kwargs): - """The default view representing the instance's management""" self.w(u'

      %s

      ' % _("Registry's content")) keys = sorted(self._cw.vreg) - self.w(u'

      %s

      \n' % ' - '.join('%s' - % (key, key) for key in keys)) + url = self._cw.url() + self.w(u'

      %s

      \n' % ' - '.join('%s' + % (url, key, key) for key in keys)) for key in keys: - self.w(u'

      %s

      ' % (key, key)) - items = self._cw.vreg[key].items() - if items: - self.w(u'') - for key, value in sorted(items): - self.w(u'' - % (key, xml_escape(repr(value)))) - self.w(u'
      %s%s
      \n') + self.w(u'

      %s

      ' % (key, key)) + if self._cw.vreg[key]: + values = sorted(self._cw.vreg[key].iteritems()) + self.wview('pyvaltable', pyvalue=[(key, xml_escape(repr(val))) + for key, val in values]) else: self.w(u'

      Empty

      \n') + + +class GCView(StartupView): + """display garbage collector information""" + __regid__ = 'gc' + __select__ = StartupView.__select__ & match_user_groups('managers') + title = _('memory leak debugging') + + def call(self, **kwargs): + from cubicweb._gcdebug import gc_info + from rql.stmts import Union + from cubicweb.appobject import AppObject + from cubicweb.rset import ResultSet + from cubicweb.dbapi import Connection, Cursor + from cubicweb.web.request import CubicWebRequestBase + lookupclasses = (AppObject, + Union, ResultSet, + Connection, Cursor, + CubicWebRequestBase) + try: + from cubicweb.server.session import Session, ChildSession, InternalSession + lookupclasses += (InternalSession, ChildSession, Session) + except ImportError: + pass # no server part installed + self.w(u'

      %s

      ' % _('Garbage collection information')) + counters, ocounters, garbage = gc_info(lookupclasses, + viewreferrersclasses=()) + self.w(u'

      %s

      ' % _('Looked up classes')) + values = sorted(counters.iteritems(), key=lambda x: x[1], reverse=True) + self.wview('pyvaltable', pyvalue=values) + self.w(u'

      %s

      ' % _('Most referenced classes')) + values = sorted(ocounters.iteritems(), key=lambda x: x[1], reverse=True) + self.wview('pyvaltable', pyvalue=values[:self._cw.form.get('nb', 20)]) + if garbage: + self.w(u'

      %s

      ' % _('Unreachable objects')) + values = sorted(xml_escape(repr(o) for o in garbage)) + self.wview('pyvallist', pyvalue=values) diff -r e402e0b32075 -r c666d265fb95 web/views/pyviews.py --- a/web/views/pyviews.py Fri Mar 12 15:05:33 2010 +0100 +++ b/web/views/pyviews.py Fri Mar 12 16:11:56 2010 +0100 @@ -17,18 +17,23 @@ def call(self, pyvalue, headers=None): if headers is None: headers = self._cw.form.get('headers') - self.w(u'\n') + w = self.w + w(u'
      \n') if headers: - self.w(u'') + w(u'') + w(u'') for header in headers: - self.w(u'' % header) - self.w(u'\n') + w(u'' % header) + w(u'\n') + w(u'') + w(u'') for row in pyvalue: - self.w(u'') + w(u'') for cell in row: - self.w(u'' % cell) - self.w(u'\n') - self.w(u'
      %s
      %s
      %s
      \n') + w(u'%s' % cell) + w(u'\n') + w(u'') + w(u'\n') class PyValListView(View): diff -r e402e0b32075 -r c666d265fb95 web/views/xmlrss.py --- a/web/views/xmlrss.py Fri Mar 12 15:05:33 2010 +0100 +++ b/web/views/xmlrss.py Fri Mar 12 16:11:56 2010 +0100 @@ -148,6 +148,7 @@ content_type = 'text/xml' http_cache_manager = httpcache.MaxAgeHTTPCacheManager cache_max_age = 60*60*2 # stay in http cache for 2 hours by default + item_vid = 'rssitem' def _open(self): req = self._cw @@ -174,7 +175,7 @@ self._close() def cell_call(self, row, col): - self.wview('rssitem', self.cw_rset, row=row, col=col) + self.wview(self.item_vid, self.cw_rset, row=row, col=col) class RSSItemView(EntityView):