--- 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
--- 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 <sylvain.thenault@logilab.fr> Tue, 02 Feb 2010 11:07:00 +0100
+
cubicweb (3.5.11-1) unstable; urgency=low
* new upstream release
--- 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
--- 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"""
--- 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)
--- 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 ###################################################
--- 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:
--- 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
--- 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"
--- 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:
--- 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) {
--- 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
--- 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)
--- 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'<ul%s class="%s">' % (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'</ul>')
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:
--- 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')),
--- 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'<h4>%s %s</h4>\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'<p>%s</p>\n' % msg)