# HG changeset patch # User Sylvain Thénault # Date 1265204792 -3600 # Node ID 294e084f12630b2067ee7b28a1459bd161e6a857 # Parent 101344a6ff9b24c71a0cc0236ce7f8f70771cd80# Parent dccf8e5eb79e7dcf57ba3cd1513bf18b2fcbb830 backport stable diff -r 101344a6ff9b -r 294e084f1263 .hgtags --- a/.hgtags Tue Feb 02 18:24:45 2010 +0100 +++ b/.hgtags Wed Feb 03 14:46:32 2010 +0100 @@ -96,3 +96,5 @@ dfe2f245248c97bea3a29c8ecc6d293e25ff708e cubicweb-debian-version-3.5.10-1 f48b2f193961803cf42147272671a335a2daeceb cubicweb-version-3.5.11 4920121d41f28c8075a4f00461911677396fc566 cubicweb-debian-version-3.5.11-1 +98af3d02b83e7635207781289cc3445fb0829951 cubicweb-version-3.5.12 +4281e1e2d76b9a37f38c0eeb1cbdcaa2fac6533c cubicweb-debian-version-3.5.12-1 diff -r 101344a6ff9b -r 294e084f1263 __pkginfo__.py diff -r 101344a6ff9b -r 294e084f1263 debian/changelog --- a/debian/changelog Tue Feb 02 18:24:45 2010 +0100 +++ b/debian/changelog Wed Feb 03 14:46:32 2010 +0100 @@ -1,3 +1,9 @@ +cubicweb (3.5.12-1) unstable; urgency=low + + * new upstream release + + -- Sylvain Thénault Tue, 02 Feb 2010 11:07:00 +0100 + cubicweb (3.5.11-1) unstable; urgency=low * new upstream release diff -r 101344a6ff9b -r 294e084f1263 devtools/__init__.py diff -r 101344a6ff9b -r 294e084f1263 doc/book/en/admin/setup.rst --- a/doc/book/en/admin/setup.rst Tue Feb 02 18:24:45 2010 +0100 +++ b/doc/book/en/admin/setup.rst Wed Feb 03 14:46:32 2010 +0100 @@ -234,9 +234,9 @@ -------------------------------- This currently assumes that the instances configurations is located -at C:\etc\cubicweb.d. +at C:\\etc\\cubicweb.d. -For a cube 'my_cube', you will then find C:\etc\cubicweb.d\my_cube\win32svc.py +For a cube 'my_cube', you will then find C:\\etc\\cubicweb.d\\my_cube\\win32svc.py that has to be used thusly:: win32svc install diff -r 101344a6ff9b -r 294e084f1263 entities/authobjs.py --- a/entities/authobjs.py Tue Feb 02 18:24:45 2010 +0100 +++ b/entities/authobjs.py Wed Feb 03 14:46:32 2010 +0100 @@ -83,7 +83,7 @@ def is_in_group(self, group): """convience / shortcut method to test if the user belongs to `group` """ - return group in self._groups + return group in self.groups def is_anonymous(self): """ checks if user is an anonymous user""" diff -r 101344a6ff9b -r 294e084f1263 entities/wfobjs.py --- a/entities/wfobjs.py Tue Feb 02 18:24:45 2010 +0100 +++ b/entities/wfobjs.py Wed Feb 03 14:46:32 2010 +0100 @@ -491,7 +491,7 @@ stateeid = statename.eid else: if not isinstance(statename, basestring): - warn('give a state name') + warn('[3.5] give a state name', DeprecationWarning) state = self.current_workflow.state_by_eid(statename) else: state = self.current_workflow.state_by_name(statename) diff -r 101344a6ff9b -r 294e084f1263 entity.py --- a/entity.py Tue Feb 02 18:24:45 2010 +0100 +++ b/entity.py Wed Feb 03 14:46:32 2010 +0100 @@ -272,7 +272,7 @@ def has_perm(self, action): return self.e_schema.has_perm(self._cw, action, eid=self.eid) - def view(self, vid, __registry='views', **kwargs): + def view(self, __vid, __registry='views', **kwargs): """shortcut to apply a view on this entity""" view = self._cw.vreg[__registry].select(vid, self._cw, rset=self.cw_rset, row=self.cw_row, col=self.cw_col, @@ -477,6 +477,7 @@ continue yield attr + _cw_completed = False def complete(self, attributes=None, skip_bytes=True): """complete this entity by adding missing attributes (i.e. query the repository to fill the entity) @@ -486,6 +487,10 @@ if true, attribute of type Bytes won't be considered """ assert self.has_eid() + if self._cw_completed: + return + if attributes is None: + self._cw_completed = True varmaker = rqlvar_maker() V = varmaker.next() rql = ['WHERE %s eid %%(x)s' % V] @@ -763,14 +768,23 @@ self._related_cache.pop('%s_%s' % (rtype, role), None) def clear_all_caches(self): + """flush all caches on this entity. Further attributes/relations access + will triggers new database queries to get back values. + + If you use custom caches on your entity class (take care to @cached!), + you should override this method to clear them as well. + """ + # clear attributes cache haseid = 'eid' in self + self._cw_completed = False self.clear() - for rschema, _, role in self.e_schema.relation_definitions(): - self.clear_related_cache(rschema.type, role) # set eid if it was in, else we may get nasty error while editing this # entity if it's bound to a repo session if haseid: self['eid'] = self.eid + # clear relations cache + for rschema, _, role in self.e_schema.relation_definitions(): + self.clear_related_cache(rschema.type, role) # raw edition utilities ################################################### diff -r 101344a6ff9b -r 294e084f1263 etwist/request.py --- a/etwist/request.py Tue Feb 02 18:24:45 2010 +0100 +++ b/etwist/request.py Wed Feb 03 14:46:32 2010 +0100 @@ -113,7 +113,7 @@ def header_if_modified_since(self): """If the HTTP header If-modified-since is set, return the equivalent - mx date time value (GMT), else return None + date time value (GMT), else return None """ mtime = self.get_header('If-modified-since', raw=False) if mtime: diff -r 101344a6ff9b -r 294e084f1263 etwist/server.py --- a/etwist/server.py Tue Feb 02 18:24:45 2010 +0100 +++ b/etwist/server.py Wed Feb 03 14:46:32 2010 +0100 @@ -10,6 +10,7 @@ import sys import os import select +import errno import hotshot from time import mktime from datetime import date, timedelta diff -r 101344a6ff9b -r 294e084f1263 i18n/fr.po --- a/i18n/fr.po Tue Feb 02 18:24:45 2010 +0100 +++ b/i18n/fr.po Wed Feb 03 14:46:32 2010 +0100 @@ -752,7 +752,11 @@ "a RQL expression which should return some results, else the transition won't " "be available. This query may use X and U variables that will respectivly " "represents the current entity and the current user" -msgstr "une expression RQL devant retourner des résultats pour que la transition puisse être passée. Cette expression peut utiliser les variables X et U qui représentent respectivement l'entité à laquelle on veut appliquer la transition et l'utilisateur courant." +msgstr "" +"une expression RQL devant retourner des résultats pour que la transition " +"puisse être passée. Cette expression peut utiliser les variables X et U qui " +"représentent respectivement l'entité à laquelle on veut appliquer la " +"transition et l'utilisateur courant." msgid "a URI representing an object in external data store" msgstr "une Uri désignant un objet dans un entrepôt de données externe" @@ -1262,7 +1266,8 @@ msgstr "boîte d'actions" msgid "boxes_edit_box_description" -msgstr "boîte affichant les différentes actions possibles sur les données affichées" +msgstr "" +"boîte affichant les différentes actions possibles sur les données affichées" msgid "boxes_filter_box" msgstr "filtrer" diff -r 101344a6ff9b -r 294e084f1263 server/sqlutils.py diff -r 101344a6ff9b -r 294e084f1263 vregistry.py --- a/vregistry.py Tue Feb 02 18:24:45 2010 +0100 +++ b/vregistry.py Wed Feb 03 14:46:32 2010 +0100 @@ -122,7 +122,6 @@ # use classid() to compare classes because vreg will probably # have its own version of the class, loaded through execfile if classid(registered) == clsid: - # XXX automatic reloading management self[oid].remove(registered) break else: diff -r 101344a6ff9b -r 294e084f1263 web/data/cubicweb.edition.js --- a/web/data/cubicweb.edition.js Tue Feb 02 18:24:45 2010 +0100 +++ b/web/data/cubicweb.edition.js Wed Feb 03 14:46:32 2010 +0100 @@ -434,7 +434,7 @@ */ function setFormsTarget(node) { var $node = jQuery(node || document.body); - $node.find('form.entityForm').each(function () { + $node.find('form').each(function () { var form = jQuery(this); var target = form.attr('cubicweb:target'); if (target) { diff -r 101344a6ff9b -r 294e084f1263 web/httpcache.py --- a/web/httpcache.py Tue Feb 02 18:24:45 2010 +0100 +++ b/web/httpcache.py Wed Feb 03 14:46:32 2010 +0100 @@ -8,6 +8,7 @@ """ __docformat__ = "restructuredtext en" +from time import mktime from datetime import datetime # time delta usable to convert localized time to GMT time @@ -23,6 +24,7 @@ def set_headers(self): self.req.set_header('Cache-control', 'no-cache') + class MaxAgeHTTPCacheManager(NoHTTPCacheManager): """max-age cache manager: set max-age cache control policy, with max-age specified with the `cache_max_age` attribute of the view @@ -31,6 +33,7 @@ self.req.set_header('Cache-control', 'max-age=%s' % self.view.cache_max_age) + class EtagHTTPCacheManager(NoHTTPCacheManager): """etag based cache manager for startup views @@ -38,8 +41,6 @@ * set policy to 'must-revalidate' and expires to the current time to force revalidation on each request """ - # GMT time required - date_format = "%a, %d %b %Y %H:%M:%S GMT" def etag(self): return self.view.__regid__ + '/' + ','.join(sorted(self.req.user.groups)) @@ -49,6 +50,7 @@ return 0 def last_modified(self): + """return view's last modified GMT time""" return self.view.last_modified() def set_headers(self): @@ -61,7 +63,12 @@ req.set_header('Cache-control', 'must-revalidate;max-age=%s' % self.max_age()) mdate = self.last_modified() - req.set_header('Last-modified', mdate.strftime(self.date_format)) + # use a timestamp, not a formatted raw header, and let + # the front-end correctly generate it + # ("%a, %d %b %Y %H:%M:%S GMT" return localized date that + # twisted don't parse correctly) + req.set_header('Last-modified', mktime(mdate.timetuple()), raw=False) + class EntityHTTPCacheManager(EtagHTTPCacheManager): """etag based cache manager for view displaying a single entity @@ -99,6 +106,7 @@ self.http_cache_manager(self).set_headers() viewmod.View.set_http_cache_headers = set_http_cache_headers + def last_modified(self): """return the date/time where this view should be considered as modified. Take care of possible related objects modifications. @@ -111,16 +119,13 @@ mtime = self._cw.header_if_modified_since() if mtime: tdelta = (ctime - mtime) - if tdelta.days * 24*60*60 + tdelta.seconds > self.cache_max_age: - mtime = ctime - else: - mtime = ctime - else: - mtime = ctime + if tdelta.days * 24*60*60 + tdelta.seconds <= self.cache_max_age: + return mtime # mtime = ctime will force page rerendering - return mtime + return ctime viewmod.View.last_modified = last_modified + # configure default caching viewmod.View.http_cache_manager = NoHTTPCacheManager # max-age=0 to actually force revalidation when needed diff -r 101344a6ff9b -r 294e084f1263 web/views/basecontrollers.py --- a/web/views/basecontrollers.py Tue Feb 02 18:24:45 2010 +0100 +++ b/web/views/basecontrollers.py Wed Feb 03 14:46:32 2010 +0100 @@ -487,6 +487,7 @@ cookies[statename] = ';'.join(marked) self._cw.set_cookie(cookies, statename) + @jsonize def js_set_cookie(self, cookiename, cookievalue): # XXX we should consider jQuery.Cookie cookiename, cookievalue = str(cookiename), str(cookievalue) diff -r 101344a6ff9b -r 294e084f1263 web/views/treeview.py --- a/web/views/treeview.py Tue Feb 02 18:24:45 2010 +0100 +++ b/web/views/treeview.py Wed Feb 03 14:46:32 2010 +0100 @@ -59,9 +59,14 @@ self._init_headers(treeid, toplevel_thru_ajax) ulid = ' id="tree-%s"' % treeid self.w(u'' % (ulid, self.css_classes)) - for rowidx in xrange(len(self.cw_rset)): - self.wview(self.itemvid, self.cw_rset, row=rowidx, col=0, - vid=subvid, parentvid=self.__regid__, treeid=treeid, **morekwargs) + for i, entity in enumerate(sorted(self.cw_rset.entities(), + key=lambda x: x.dc_title())): + if i+1 < len(self.cw_rset): + morekwargs['is_last'] = False + else: + morekwargs['is_last'] = True + entity.view(self.itemvid, vid=subvid, parentvid=self.__regid__, + treeid=treeid, w=self.w, **morekwargs) self.w(u'') def cell_call(self, *args, **allargs): @@ -71,6 +76,7 @@ allargs.pop('col') self.call(*args, **allargs) + class FileTreeView(TreeView): """specific version of the treeview to display file trees """ @@ -103,7 +109,7 @@ """default treeitem view for entities which don't implement ITree""" __regid__ = 'treeitemview' - def cell_call(self, row, col, vid='oneline', parentvid='treeview', treeid=None): + def cell_call(self, row, col, vid='oneline', treeid=None, **morekwargs): assert treeid is not None entity = self.cw_rset.get_entity(row, col) itemview = self._cw.view(vid, self.cw_rset, row=row, col=col) @@ -129,11 +135,11 @@ return str(eeid) in treestate.value.split(';') return self.default_branch_state_is_open - def cell_call(self, row, col, treeid, vid='oneline', parentvid='treeview', **morekwargs): + def cell_call(self, row, col, treeid, vid='oneline', parentvid='treeview', + is_last=False, **morekwargs): w = self.w entity = self.cw_rset.get_entity(row, col) liclasses = [] - is_last = row == len(self.cw_rset) - 1 is_open = self.open_state(entity.eid, treeid) is_leaf = not hasattr(entity, 'is_leaf') or entity.is_leaf() if is_leaf: diff -r 101344a6ff9b -r 294e084f1263 web/views/urlrewrite.py --- a/web/views/urlrewrite.py Tue Feb 02 18:24:45 2010 +0100 +++ b/web/views/urlrewrite.py Wed Feb 03 14:46:32 2010 +0100 @@ -83,6 +83,8 @@ ('/notfound', dict(vid='404')), ('/error', dict(vid='error')), ('/sparql', dict(vid='sparql')), + # XXX should be case insensitive as 'create', but I would like to find another way than + # relying on the etype_selector (rgx('/schema/([^/]+?)/?'), dict(vid='primary', rql=r'Any X WHERE X is CWEType, X name "\1"')), (rgx('/add/([^/]+?)/?'), dict(vid='creation', etype=r'\1')), (rgx('/doc/images/(.+?)/?'), dict(vid='wdocimages', fid=r'\1')), diff -r 101344a6ff9b -r 294e084f1263 web/views/workflow.py --- a/web/views/workflow.py Tue Feb 02 18:24:45 2010 +0100 +++ b/web/views/workflow.py Wed Feb 03 14:46:32 2010 +0100 @@ -67,7 +67,7 @@ form = self.get_form(entity, transition) self.w(u'

%s %s

\n' % (self._cw._(transition.name), entity.view('oneline'))) - msg = _('status will change from %(st1)s to %(st2)s') % { + msg = self.req._('status will change from %(st1)s to %(st2)s') % { 'st1': entity.printable_state, 'st2': self._cw._(transition.destination().name)} self.w(u'

%s

\n' % msg)