--- a/cwvreg.py Tue Aug 10 18:39:48 2010 +0200
+++ b/cwvreg.py Wed Aug 11 10:08:48 2010 +0200
@@ -713,7 +713,7 @@
vocab = pdef['vocabulary']
if vocab is not None:
if callable(vocab):
- vocab = vocab(key, None) # XXX need a req object
+ vocab = vocab(None) # XXX need a req object
if not value in vocab:
raise ValueError(_('unauthorized value'))
return value
--- a/dbapi.py Tue Aug 10 18:39:48 2010 +0200
+++ b/dbapi.py Wed Aug 11 10:08:48 2010 +0200
@@ -313,19 +313,17 @@
# low level session data management #######################################
- def get_shared_data(self, key, default=None, pop=False):
- """return value associated to `key` in shared data"""
- return self.cnx.get_shared_data(key, default, pop)
-
- def set_shared_data(self, key, value, querydata=False):
- """set value associated to `key` in shared data
+ def get_shared_data(self, key, default=None, pop=False, txdata=False):
+ """see :meth:`Connection.get_shared_data`"""
+ return self.cnx.get_shared_data(key, default, pop, txdata)
- if `querydata` is true, the value will be added to the repository
- session's query data which are cleared on commit/rollback of the current
- transaction, and won't be available through the connexion, only on the
- repository side.
- """
- return self.cnx.set_shared_data(key, value, querydata)
+ def set_shared_data(self, key, value, txdata=False, querydata=None):
+ """see :meth:`Connection.set_shared_data`"""
+ if querydata is not None:
+ txdata = querydata
+ warn('[3.10] querydata argument has been renamed to txdata',
+ DeprecationWarning, stacklevel=2)
+ return self.cnx.set_shared_data(key, value, txdata)
# server session compat layer #############################################
@@ -507,10 +505,12 @@
return DBAPIRequest(self.vreg, DBAPISession(self))
def check(self):
- """raise `BadConnectionId` if the connection is no more valid"""
+ """raise `BadConnectionId` if the connection is no more valid, else
+ return its latest activity timestamp.
+ """
if self._closed is not None:
raise ProgrammingError('Closed connection')
- self._repo.check_session(self.sessionid)
+ return self._repo.check_session(self.sessionid)
def set_session_props(self, **props):
"""raise `BadConnectionId` if the connection is no more valid"""
@@ -518,23 +518,29 @@
raise ProgrammingError('Closed connection')
self._repo.set_session_props(self.sessionid, props)
- def get_shared_data(self, key, default=None, pop=False):
- """return value associated to `key` in shared data"""
- if self._closed is not None:
- raise ProgrammingError('Closed connection')
- return self._repo.get_shared_data(self.sessionid, key, default, pop)
+ def get_shared_data(self, key, default=None, pop=False, txdata=False):
+ """return value associated to key in the session's data dictionary or
+ session's transaction's data if `txdata` is true.
- def set_shared_data(self, key, value, querydata=False):
- """set value associated to `key` in shared data
+ If pop is True, value will be removed from the dictionnary.
- if `querydata` is true, the value will be added to the repository
- session's query data which are cleared on commit/rollback of the current
- transaction, and won't be available through the connexion, only on the
- repository side.
+ If key isn't defined in the dictionnary, value specified by the
+ `default` argument will be returned.
"""
if self._closed is not None:
raise ProgrammingError('Closed connection')
- return self._repo.set_shared_data(self.sessionid, key, value, querydata)
+ return self._repo.get_shared_data(self.sessionid, key, default, pop, txdata)
+
+ def set_shared_data(self, key, value, txdata=False):
+ """set value associated to `key` in shared data
+
+ if `txdata` is true, the value will be added to the repository session's
+ transaction's data which are cleared on commit/rollback of the current
+ transaction.
+ """
+ if self._closed is not None:
+ raise ProgrammingError('Closed connection')
+ return self._repo.set_shared_data(self.sessionid, key, value, txdata)
def get_schema(self):
"""Return the schema currently used by the repository.
--- a/entities/__init__.py Tue Aug 10 18:39:48 2010 +0200
+++ b/entities/__init__.py Wed Aug 11 10:08:48 2010 +0200
@@ -35,6 +35,11 @@
__regid__ = 'Any'
__implements__ = ()
+ @classmethod
+ def cw_create_url(cls, req, **kwargs):
+ """ return the url of the entity creation form for this entity type"""
+ return req.build_url('add/%s' % cls.__regid__, **kwargs)
+
# meta data api ###########################################################
def dc_title(self):
--- a/etwist/twconfig.py Tue Aug 10 18:39:48 2010 +0200
+++ b/etwist/twconfig.py Wed Aug 11 10:08:48 2010 +0200
@@ -76,12 +76,6 @@
the repository rather than the user running the command',
'group': 'main', 'level': WebConfiguration.mode == 'system'
}),
- ('session-time',
- {'type' : 'time',
- 'default': '30min',
- 'help': 'session expiration time, default to 30 minutes',
- 'group': 'main', 'level': 1,
- }),
('pyro-server',
{'type' : 'yn',
# pyro is only a recommends by default, so don't activate it here
--- a/goa/appobjects/components.py Tue Aug 10 18:39:48 2010 +0200
+++ b/goa/appobjects/components.py Wed Aug 11 10:08:48 2010 +0200
@@ -15,9 +15,8 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
-"""overrides some base views for cubicweb on google appengine
+"""overrides some base views for cubicweb on google appengine"""
-"""
__docformat__ = "restructuredtext en"
from logilab.mtconverter import xml_escape
@@ -88,7 +87,8 @@
view = self.vreg.select('views', 'list', req, req.etype_rset(etype))
url = view.url()
etypelink = u' <a href="%s">%s</a>' % (xml_escape(url), label)
- yield (label, etypelink, self.add_entity_link(eschema, req))
+ if eschema.has_perm(req, 'add'):
+ yield (label, etypelink, self.add_entity_link(etype))
ManageView.entity_types = entity_types_no_count
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.10.0_common.py Wed Aug 11 10:08:48 2010 +0200
@@ -0,0 +1,1 @@
+option_group_changed('cleanup-session-time', 'web', 'main')
--- a/server/repository.py Tue Aug 10 18:39:48 2010 +0200
+++ b/server/repository.py Wed Aug 11 10:08:48 2010 +0200
@@ -267,7 +267,10 @@
# call instance level initialisation hooks
self.hm.call_hooks('server_startup', repo=self)
# register a task to cleanup expired session
- self.looping_task(self.config['session-time']/3., self.clean_sessions)
+ self.cleanup_session_time = self.config['cleanup-session-time'] or 60 * 60 * 24
+ assert self.cleanup_session_time > 0
+ cleanup_session_interval = min(60*60, self.cleanup_session_time / 3)
+ self.looping_task(cleanup_session_interval, self.clean_sessions)
assert isinstance(self._looping_tasks, list), 'already started'
for i, (interval, func, args) in enumerate(self._looping_tasks):
self._looping_tasks[i] = task = utils.LoopTask(interval, func, args)
@@ -622,24 +625,32 @@
session.reset_pool()
def check_session(self, sessionid):
- """raise `BadConnectionId` if the connection is no more valid"""
- self._get_session(sessionid, setpool=False)
+ """raise `BadConnectionId` if the connection is no more valid, else
+ return its latest activity timestamp.
+ """
+ return self._get_session(sessionid, setpool=False).timestamp
+
+ def get_shared_data(self, sessionid, key, default=None, pop=False, txdata=False):
+ """return value associated to key in the session's data dictionary or
+ session's transaction's data if `txdata` is true.
- def get_shared_data(self, sessionid, key, default=None, pop=False):
- """return the session's data dictionary"""
+ If pop is True, value will be removed from the dictionnary.
+
+ If key isn't defined in the dictionnary, value specified by the
+ `default` argument will be returned.
+ """
session = self._get_session(sessionid, setpool=False)
- return session.get_shared_data(key, default, pop)
+ return session.get_shared_data(key, default, pop, txdata)
- def set_shared_data(self, sessionid, key, value, querydata=False):
+ def set_shared_data(self, sessionid, key, value, txdata=False):
"""set value associated to `key` in shared data
- if `querydata` is true, the value will be added to the repository
- session's query data which are cleared on commit/rollback of the current
- transaction, and won't be available through the connexion, only on the
- repository side.
+ if `txdata` is true, the value will be added to the repository session's
+ transaction's data which are cleared on commit/rollback of the current
+ transaction.
"""
session = self._get_session(sessionid, setpool=False)
- session.set_shared_data(key, value, querydata)
+ session.set_shared_data(key, value, txdata)
def commit(self, sessionid, txid=None):
"""commit transaction for the session with the given id"""
@@ -771,7 +782,7 @@
"""close sessions not used since an amount of time specified in the
configuration
"""
- mintime = time() - self.config['session-time']
+ mintime = time() - self.cleanup_session_time
self.debug('cleaning session unused since %s',
strftime('%T', localtime(mintime)))
nbclosed = 0
--- a/server/serverconfig.py Tue Aug 10 18:39:48 2010 +0200
+++ b/server/serverconfig.py Wed Aug 11 10:08:48 2010 +0200
@@ -120,10 +120,16 @@
the repository rather than the user running the command',
'group': 'main', 'level': (CubicWebConfiguration.mode == 'installed') and 0 or 1,
}),
- ('session-time',
+ ('cleanup-session-time',
{'type' : 'time',
- 'default': '30min',
- 'help': 'session expiration time, default to 30 minutes',
+ 'default': '24h',
+ 'help': 'duration of inactivity after which a session '
+ 'will be closed, to limit memory consumption (avoid sessions that '
+ 'never expire and cause memory leak when http-session-time is 0, or '
+ 'because of bad client that never closes their connection). '
+ 'So notice that even if http-session-time is 0 and the user don\'t '
+ 'close his browser, he will have to reauthenticate after this time '
+ 'of inactivity. Default to 24h.',
'group': 'main', 'level': 3,
}),
('connections-pool-size',
--- a/server/session.py Tue Aug 10 18:39:48 2010 +0200
+++ b/server/session.py Wed Aug 11 10:08:48 2010 +0200
@@ -618,16 +618,20 @@
# shared data handling ###################################################
- def get_shared_data(self, key, default=None, pop=False):
+ def get_shared_data(self, key, default=None, pop=False, txdata=False):
"""return value associated to `key` in session data"""
- if pop:
- return self.data.pop(key, default)
+ if txdata:
+ data = self.transaction_data
else:
- return self.data.get(key, default)
+ data = self.data
+ if pop:
+ return data.pop(key, default)
+ else:
+ return data.get(key, default)
- def set_shared_data(self, key, value, querydata=False):
+ def set_shared_data(self, key, value, txdata=False):
"""set value associated to `key` in session data"""
- if querydata:
+ if txdata:
self.transaction_data[key] = value
else:
self.data[key] = value
--- a/server/test/data/schema.py Tue Aug 10 18:39:48 2010 +0200
+++ b/server/test/data/schema.py Wed Aug 11 10:08:48 2010 +0200
@@ -17,7 +17,8 @@
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
from yams.buildobjs import (EntityType, RelationType, RelationDefinition,
- SubjectRelation, RichString, String, Int, Boolean, Datetime)
+ SubjectRelation, RichString, String, Int, Float,
+ Boolean, Datetime)
from yams.constraints import SizeConstraint
from cubicweb.schema import (WorkflowableEntityType, RQLConstraint,
ERQLExpression, RRQLExpression)
@@ -39,7 +40,7 @@
description=_('more detailed description'))
duration = Int()
- invoiced = Int()
+ invoiced = Float()
depends_on = SubjectRelation('Affaire')
require_permission = SubjectRelation('CWPermission')
--- a/server/test/unittest_msplanner.py Tue Aug 10 18:39:48 2010 +0200
+++ b/server/test/unittest_msplanner.py Wed Aug 11 10:08:48 2010 +0200
@@ -1866,7 +1866,7 @@
[('FetchStep', [('Any WP WHERE 999999 multisource_rel WP, WP is Note', [{'WP': 'Note'}])],
[self.cards], None, {'WP': u'table0.C0'}, []),
('OneFetchStep', [('Any S,SUM(DUR),SUM(I),(SUM(I) - SUM(DUR)),MIN(DI),MAX(DI) GROUPBY S ORDERBY S WHERE A duration DUR, A invoiced I, A modification_date DI, A in_state S, S name SN, (EXISTS(A concerne WP, WP is Note)) OR (EXISTS(A concerne 999999)), A is Affaire, S is State',
- [{'A': 'Affaire', 'DI': 'Datetime', 'DUR': 'Int', 'I': 'Int', 'S': 'State', 'SN': 'String', 'WP': 'Note'}])],
+ [{'A': 'Affaire', 'DI': 'Datetime', 'DUR': 'Int', 'I': 'Float', 'S': 'State', 'SN': 'String', 'WP': 'Note'}])],
None, None, [self.system], {'WP': u'table0.C0'}, [])],
{'n': 999999})
--- a/server/test/unittest_querier.py Tue Aug 10 18:39:48 2010 +0200
+++ b/server/test/unittest_querier.py Wed Aug 11 10:08:48 2010 +0200
@@ -544,12 +544,25 @@
self.assertEquals(rset.rows[0][0], 'ADMIN')
self.assertEquals(rset.description, [('String',)])
-## def test_select_simplified(self):
-## ueid = self.session.user.eid
-## rset = self.execute('Any L WHERE %s login L'%ueid)
-## self.assertEquals(rset.rows[0][0], 'admin')
-## rset = self.execute('Any L WHERE %(x)s login L', {'x':ueid})
-## self.assertEquals(rset.rows[0][0], 'admin')
+ def test_select_float_abs(self):
+ # test positive number
+ eid = self.execute('INSERT Affaire A: A invoiced %(i)s', {'i': 1.2})[0][0]
+ rset = self.execute('Any ABS(I) WHERE X eid %(x)s, X invoiced I', {'x': eid})
+ self.assertEquals(rset.rows[0][0], 1.2)
+ # test negative number
+ eid = self.execute('INSERT Affaire A: A invoiced %(i)s', {'i': -1.2})[0][0]
+ rset = self.execute('Any ABS(I) WHERE X eid %(x)s, X invoiced I', {'x': eid})
+ self.assertEquals(rset.rows[0][0], 1.2)
+
+ def test_select_int_abs(self):
+ # test positive number
+ eid = self.execute('INSERT Affaire A: A duration %(d)s', {'d': 12})[0][0]
+ rset = self.execute('Any ABS(D) WHERE X eid %(x)s, X duration D', {'x': eid})
+ self.assertEquals(rset.rows[0][0], 12)
+ # test negative number
+ eid = self.execute('INSERT Affaire A: A duration %(d)s', {'d': -12})[0][0]
+ rset = self.execute('Any ABS(D) WHERE X eid %(x)s, X duration D', {'x': eid})
+ self.assertEquals(rset.rows[0][0], 12)
def test_select_searchable_text_1(self):
rset = self.execute(u"INSERT Personne X: X nom 'bidüle'")
--- a/server/test/unittest_repository.py Tue Aug 10 18:39:48 2010 +0200
+++ b/server/test/unittest_repository.py Wed Aug 11 10:08:48 2010 +0200
@@ -161,7 +161,7 @@
def test_check_session(self):
repo = self.repo
cnxid = repo.connect(self.admlogin, password=self.admpassword)
- self.assertEquals(repo.check_session(cnxid), None)
+ self.assertIsInstance(repo.check_session(cnxid), float)
repo.close(cnxid)
self.assertRaises(BadConnectionId, repo.check_session, cnxid)
--- a/utils.py Tue Aug 10 18:39:48 2010 +0200
+++ b/utils.py Wed Aug 11 10:08:48 2010 +0200
@@ -24,6 +24,7 @@
import decimal
import datetime
import random
+from inspect import getargspec
from itertools import repeat
from uuid import uuid4
from warnings import warn
@@ -64,6 +65,15 @@
'__doc__': cls.__doc__,
'__module__': cls.__module__})
+def support_args(callable, *argnames):
+ """return true if the callable support given argument names"""
+ argspec = getargspec(callable)
+ if argspec[2]:
+ return True
+ for argname in argnames:
+ if argname not in argspec[0]:
+ return False
+ return True
# use networkX instead ?
# http://networkx.lanl.gov/reference/algorithms.traversal.html#module-networkx.algorithms.traversal.astar
--- a/view.py Tue Aug 10 18:39:48 2010 +0200
+++ b/view.py Wed Aug 11 10:08:48 2010 +0200
@@ -319,21 +319,11 @@
clabel = vtitle
return u'%s (%s)' % (clabel, self._cw.property_value('ui.site-title'))
- def output_url_builder( self, name, url, args ):
- self.w(u'<script language="JavaScript"><!--\n' \
- u'function %s( %s ) {\n' % (name, ','.join(args) ) )
- url_parts = url.split("%s")
- self.w(u' url="%s"' % url_parts[0] )
- for arg, part in zip(args, url_parts[1:]):
- self.w(u'+str(%s)' % arg )
- if part:
- self.w(u'+"%s"' % part)
- self.w('\n document.window.href=url;\n')
- self.w('}\n-->\n</script>\n')
-
+ @deprecated('[3.10] use vreg["etypes"].etype_class(etype).cw_create_url(req)')
def create_url(self, etype, **kwargs):
""" return the url of the entity creation form for a given entity type"""
- return self._cw.build_url('add/%s' % etype, **kwargs)
+ return self._cw.vreg["etypes"].etype_class(etype).cw_create_url(
+ self._cw, **kwargs)
def field(self, label, value, row=True, show_label=True, w=None, tr=True,
table=False):
--- a/vregistry.py Tue Aug 10 18:39:48 2010 +0200
+++ b/vregistry.py Wed Aug 11 10:08:48 2010 +0200
@@ -173,7 +173,7 @@
assert len(objects) == 1, objects
return objects[0](*args, **kwargs)
- def select(self, oid, *args, **kwargs):
+ def select(self, __oid, *args, **kwargs):
"""return the most specific object among those with the given oid
according to the given context.
@@ -181,14 +181,14 @@
raise :exc:`NoSelectableObject` if not object apply
"""
- return self._select_best(self[oid], *args, **kwargs)
+ return self._select_best(self[__oid], *args, **kwargs)
- def select_or_none(self, oid, *args, **kwargs):
+ def select_or_none(self, __oid, *args, **kwargs):
"""return the most specific object among those with the given oid
according to the given context, or None if no object applies.
"""
try:
- return self.select(oid, *args, **kwargs)
+ return self.select(__oid, *args, **kwargs)
except (NoSelectableObject, ObjectNotFound):
return None
select_object = deprecated('[3.6] use select_or_none instead of select_object'
--- a/web/application.py Tue Aug 10 18:39:48 2010 +0200
+++ b/web/application.py Wed Aug 11 10:08:48 2010 +0200
@@ -31,7 +31,7 @@
from cubicweb import set_log_methods, cwvreg
from cubicweb import (
ValidationError, Unauthorized, AuthenticationError, NoSelectableObject,
- RepositoryError, CW_EVENT_MANAGER)
+ RepositoryError, BadConnectionId, CW_EVENT_MANAGER)
from cubicweb.dbapi import DBAPISession
from cubicweb.web import LOGGER, component
from cubicweb.web import (
@@ -48,48 +48,43 @@
def __init__(self, vreg):
self.session_time = vreg.config['http-session-time'] or None
- if self.session_time is not None:
- assert self.session_time > 0
- self.cleanup_session_time = self.session_time
- else:
- self.cleanup_session_time = vreg.config['cleanup-session-time'] or 1440 * 60
- assert self.cleanup_session_time > 0
- self.cleanup_anon_session_time = vreg.config['cleanup-anonymous-session-time'] or 5 * 60
- assert self.cleanup_anon_session_time > 0
self.authmanager = vreg['components'].select('authmanager', vreg=vreg)
+ interval = (self.session_time or 0) / 2.
if vreg.config.anonymous_user() is not None:
- self.clean_sessions_interval = max(
- 5 * 60, min(self.cleanup_session_time / 2.,
- self.cleanup_anon_session_time / 2.))
- else:
- self.clean_sessions_interval = max(
- 5 * 60,
- self.cleanup_session_time / 2.)
+ self.cleanup_anon_session_time = vreg.config['cleanup-anonymous-session-time'] or 5 * 60
+ assert self.cleanup_anon_session_time > 0
+ if self.session_time is not None:
+ self.cleanup_anon_session_time = min(self.session_time,
+ self.cleanup_anon_session_time)
+ interval = self.cleanup_anon_session_time / 2.
+ # we don't want to check session more than once every 5 minutes
+ self.clean_sessions_interval = max(5 * 60, interval)
def clean_sessions(self):
"""cleanup sessions which has not been unused since a given amount of
time. Return the number of sessions which have been closed.
"""
self.debug('cleaning http sessions')
+ session_time = self.session_time
closed, total = 0, 0
for session in self.current_sessions():
- no_use_time = (time() - session.last_usage_time)
total += 1
- if session.anonymous_session:
- if no_use_time >= self.cleanup_anon_session_time:
+ try:
+ last_usage_time = session.cnx.check()
+ except BadConnectionId:
+ self.close_session(session)
+ closed += 1
+ else:
+ no_use_time = (time() - last_usage_time)
+ if session.anonymous_session:
+ if no_use_time >= self.cleanup_anon_session_time:
+ self.close_session(session)
+ closed += 1
+ elif session_time is not None and no_use_time >= session_time:
self.close_session(session)
closed += 1
- elif no_use_time >= self.cleanup_session_time:
- self.close_session(session)
- closed += 1
return closed, total - closed
- def has_expired(self, session):
- """return True if the web session associated to the session is expired
- """
- return not (self.session_time is None or
- time() < session.last_usage_time + self.session_time)
-
def current_sessions(self):
"""return currently open sessions"""
raise NotImplementedError()
@@ -213,8 +208,6 @@
except AuthenticationError:
req.remove_cookie(cookie, self.SESSION_VAR)
raise
- # remember last usage time for web session tracking
- session.last_usage_time = time()
def get_session(self, req, sessionid):
return self.session_manager.get_session(req, sessionid)
@@ -224,8 +217,6 @@
cookie = req.get_cookie()
cookie[self.SESSION_VAR] = session.sessionid
req.set_cookie(cookie, self.SESSION_VAR, maxage=None)
- # remember last usage time for web session tracking
- session.last_usage_time = time()
if not session.anonymous_session:
self._postlogin(req)
return session
--- a/web/formfields.py Tue Aug 10 18:39:48 2010 +0200
+++ b/web/formfields.py Wed Aug 11 10:08:48 2010 +0200
@@ -73,6 +73,7 @@
FormatConstraint)
from cubicweb import Binary, tags, uilib
+from cubicweb.utils import support_args
from cubicweb.web import INTERNAL_FIELD_VALUE, ProcessFormError, eid_param, \
formwidgets as fw, uicfg
@@ -345,7 +346,12 @@
def initial_typed_value(self, form, load_bytes):
if self.value is not _MARKER:
if callable(self.value):
- return self.value(form)
+ if support_args(self.value, 'form', 'field'):
+ return self.value(form, self)
+ else:
+ warn("[3.10] field's value callback must now take form and field as argument",
+ DeprecationWarning)
+ return self.value(form)
return self.value
formattr = '%s_%s_default' % (self.role, self.name)
if hasattr(form, formattr):
--- a/web/test/unittest_session.py Tue Aug 10 18:39:48 2010 +0200
+++ b/web/test/unittest_session.py Wed Aug 11 10:08:48 2010 +0200
@@ -7,10 +7,11 @@
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
"""
from cubicweb.devtools.testlib import CubicWebTC
+from cubicweb.web import InvalidSession
class SessionTC(CubicWebTC):
- def test_auto_reconnection(self):
+ def test_session_expiration(self):
sm = self.app.session_handler.session_manager
# make is if the web session has been opened by the session manager
sm._sessions[self.cnx.sessionid] = self.websession
@@ -23,11 +24,8 @@
# fake an incoming http query with sessionid in session cookie
# don't use self.request() which try to call req.set_session
req = self.requestcls(self.vreg)
- websession = sm.get_session(req, sessionid)
- self.assertEquals(len(sm._sessions), 1)
- self.assertIs(websession, self.websession)
- self.assertEquals(websession.sessionid, sessionid)
- self.assertNotEquals(websession.sessionid, websession.cnx.sessionid)
+ self.assertRaises(InvalidSession, sm.get_session, req, sessionid)
+ self.assertEquals(len(sm._sessions), 0)
finally:
# avoid error in tearDown by telling this connection is closed...
self.cnx._closed = True
--- a/web/uicfg.py Tue Aug 10 18:39:48 2010 +0200
+++ b/web/uicfg.py Wed Aug 11 10:08:48 2010 +0200
@@ -107,11 +107,8 @@
def init_primaryview_display_ctrl(rtag, sschema, rschema, oschema, role):
if role == 'subject':
oschema = '*'
- label = rschema.type
else:
sschema = '*'
- label = '%s_%s' % (rschema, role)
- rtag.setdefault((sschema, rschema, oschema, role), 'label', label)
rtag.counter += 1
rtag.setdefault((sschema, rschema, oschema, role), 'order', rtag.counter)
--- a/web/views/authentication.py Tue Aug 10 18:39:48 2010 +0200
+++ b/web/views/authentication.py Wed Aug 11 10:08:48 2010 +0200
@@ -74,7 +74,7 @@
self.repo = vreg.config.repository(vreg)
self.log_queries = vreg.config['query-log-file']
self.authinforetreivers = sorted(vreg['webauth'].possible_objects(vreg),
- key=lambda x: x.order)
+ key=lambda x: x.order)
# 2-uple login / password, login is None when no anonymous access
# configured
self.anoninfo = vreg.config.anonymous_user()
@@ -98,25 +98,11 @@
if login and session.login != login:
raise InvalidSession('login mismatch')
try:
- lock = session.reconnection_lock
- except AttributeError:
- lock = session.reconnection_lock = Lock()
- # need to be locked two avoid duplicated reconnections on concurrent
- # requests
- with lock:
- cnx = session.cnx
- try:
- # calling cnx.user() check connection validity, raise
- # BadConnectionId on failure
- user = cnx.user(req)
- except BadConnectionId:
- # check if a connection should be automatically restablished
- if (login is None or login == session.login):
- cnx = self._authenticate(session.login, session.authinfo)
- user = cnx.user(req)
- session.cnx = cnx
- else:
- raise InvalidSession('bad connection id')
+ # calling cnx.user() check connection validity, raise
+ # BadConnectionId on failure
+ user = session.cnx.user(req)
+ except BadConnectionId:
+ raise InvalidSession('bad connection id')
return user
def authenticate(self, req):
--- a/web/views/bookmark.py Tue Aug 10 18:39:48 2010 +0200
+++ b/web/views/bookmark.py Wed Aug 11 10:08:48 2010 +0200
@@ -115,7 +115,8 @@
path = req.relative_path()
# XXX if vtitle specified in params, extract it and use it as default value
# for bookmark's title
- url = self.create_url(self.etype, __linkto=linkto, path=path)
+ url = req.vreg['etypes'].etype_class('Bookmark').cw_create_url(
+ req, __linkto=linkto, path=path)
boxmenu.append(self.mk_action(req._('bookmark this page'), url,
category='manage', id='bookmark'))
if rset:
--- a/web/views/cwproperties.py Tue Aug 10 18:39:48 2010 +0200
+++ b/web/views/cwproperties.py Wed Aug 11 10:08:48 2010 +0200
@@ -353,7 +353,7 @@
if vocab is not None:
if callable(vocab):
# list() just in case its a generator function
- self.choices = list(vocab(form._cw))
+ self.choices = list(vocab())
else:
self.choices = vocab
wdg = Select()
--- a/web/views/debug.py Tue Aug 10 18:39:48 2010 +0200
+++ b/web/views/debug.py Wed Aug 11 10:08:48 2010 +0200
@@ -119,10 +119,15 @@
if sessions:
w(u'<ul>')
for session in sessions:
+ try:
+ last_usage_time = session.cnx.check()
+ except BadConnectionId:
+ w(u'<li>%s (INVALID)</li>' % session.sessionid)
+ continue
w(u'<li>%s (%s: %s)<br/>' % (
session.sessionid,
_('last usage'),
- strftime(dtformat, localtime(session.last_usage_time))))
+ strftime(dtformat, localtime(last_usage_time))))
dict_to_html(w, session.data)
w(u'</li>')
w(u'</ul>')
--- a/web/views/editcontroller.py Tue Aug 10 18:39:48 2010 +0200
+++ b/web/views/editcontroller.py Wed Aug 11 10:08:48 2010 +0200
@@ -115,7 +115,7 @@
form = req.form
# so we're able to know the main entity from the repository side
if '__maineid' in form:
- req.set_shared_data('__maineid', form['__maineid'], querydata=True)
+ req.set_shared_data('__maineid', form['__maineid'], txdata=True)
# no specific action, generic edition
self._to_create = req.data['eidmap'] = {}
self._pending_fields = req.data['pendingfields'] = set()
--- a/web/views/editforms.py Tue Aug 10 18:39:48 2010 +0200
+++ b/web/views/editforms.py Wed Aug 11 10:08:48 2010 +0200
@@ -169,7 +169,9 @@
def url(self):
"""return the url associated with this view"""
- return self.create_url(self._cw.form.get('etype'))
+ req = self._cw
+ return req.vreg["etypes"].etype_class(req.form['etype']).cw_create_url(
+ req)
def submited_message(self):
"""return the message that will be displayed on successful edition"""
--- a/web/views/facets.py Tue Aug 10 18:39:48 2010 +0200
+++ b/web/views/facets.py Wed Aug 11 10:08:48 2010 +0200
@@ -97,9 +97,9 @@
return
if vid is None:
vid = req.form.get('vid')
- if self.bk_linkbox_template:
- self.display_bookmark_link(rset)
w = self.w
+ if self.bk_linkbox_template and req.vreg.schema['Bookmark'].has_perm(req, 'add'):
+ w(self.bookmark_link(rset))
w(u'<form method="post" id="%sForm" cubicweb:facetargs="%s" action="">' % (
divid, xml_escape(json_dumps([divid, vid, paginate, self.facetargs()]))))
w(u'<fieldset>')
@@ -113,22 +113,22 @@
wdg.render(w=self.w)
w(u'</fieldset>\n</form>\n')
- def display_bookmark_link(self, rset):
- eschema = self._cw.vreg.schema.eschema('Bookmark')
- if eschema.has_perm(self._cw, 'add'):
- bk_path = 'rql=%s' % self._cw.url_quote(rset.printable_rql())
- if self._cw.form.get('vid'):
- bk_path += '&vid=%s' % self._cw.url_quote(self._cw.form['vid'])
- bk_path = 'view?' + bk_path
- bk_title = self._cw._('my custom search')
- linkto = 'bookmarked_by:%s:subject' % self._cw.user.eid
- bk_add_url = self._cw.build_url('add/Bookmark', path=bk_path, title=bk_title, __linkto=linkto)
- bk_base_url = self._cw.build_url('add/Bookmark', title=bk_title, __linkto=linkto)
- bk_link = u'<a cubicweb:target="%s" id="facetBkLink" href="%s">%s</a>' % (
- xml_escape(bk_base_url),
- xml_escape(bk_add_url),
- self._cw._('bookmark this search'))
- self.w(self.bk_linkbox_template % bk_link)
+ def bookmark_link(self, rset):
+ req = self._cw
+ bk_path = u'rql=%s' % req.url_quote(rset.printable_rql())
+ if req.form.get('vid'):
+ bk_path += u'&vid=%s' % req.url_quote(req.form['vid'])
+ bk_path = u'view?' + bk_path
+ bk_title = req._('my custom search')
+ linkto = u'bookmarked_by:%s:subject' % req.user.eid
+ bkcls = req.vreg['etypes'].etype_class('Bookmark')
+ bk_add_url = bkcls.cw_create_url(req, path=bk_path, title=bk_title,
+ __linkto=linkto)
+ bk_base_url = bkcls.cw_create_url(req, title=bk_title, __linkto=linkto)
+ bk_link = u'<a cubicweb:target="%s" id="facetBkLink" href="%s">%s</a>' % (
+ xml_escape(bk_base_url), xml_escape(bk_add_url),
+ req._('bookmark this search'))
+ return self.bk_linkbox_template % bk_link
def get_facets(self, rset, rqlst, mainvar):
return self._cw.vreg['facets'].poss_visible_objects(
--- a/web/views/formrenderers.py Tue Aug 10 18:39:48 2010 +0200
+++ b/web/views/formrenderers.py Wed Aug 11 10:08:48 2010 +0200
@@ -41,7 +41,7 @@
from cubicweb import tags
from cubicweb.appobject import AppObject
from cubicweb.selectors import is_instance, yes
-from cubicweb.utils import json_dumps
+from cubicweb.utils import json_dumps, support_args
from cubicweb.web import eid_param, formwidgets as fwdgs
@@ -53,6 +53,8 @@
name, value, checked, attrs)
def field_label(form, field):
+ if callable(field.label):
+ return field.label(form, field)
# XXX with 3.6 we can now properly rely on 'if field.role is not None' and
# stop having a tuple for label
if isinstance(field.label, tuple): # i.e. needs contextual translation
@@ -133,7 +135,12 @@
help = []
descr = field.help
if callable(descr):
- descr = descr(form)
+ if support_args(descr, 'form', 'field'):
+ descr = descr(form, field)
+ else:
+ warn("[3.10] field's help callback must now take form and field as argument",
+ DeprecationWarning)
+ descr = descr(form)
if descr:
help.append('<div class="helper">%s</div>' % self._cw._(descr))
example = field.example_format(self._cw)
--- a/web/views/primary.py Tue Aug 10 18:39:48 2010 +0200
+++ b/web/views/primary.py Wed Aug 11 10:08:48 2010 +0200
@@ -25,6 +25,7 @@
from logilab.mtconverter import xml_escape
from cubicweb import Unauthorized
+from cubicweb.utils import support_args
from cubicweb.selectors import match_kwargs
from cubicweb.view import EntityView
from cubicweb.schema import VIRTUAL_RTYPES, display_name
@@ -143,10 +144,15 @@
if display_attributes:
self.w(u'<table>')
for rschema, role, dispctrl, value in display_attributes:
- try:
- self._render_attribute(dispctrl, rschema, value,
- role=role, table=True)
- except TypeError:
+ if support_args(self._render_attribute, 'label'):
+ label = self._rel_label(entity, rschema, role, dispctrl)
+ self._render_attribute(label, value, table=True)
+ elif support_args(self._render_attribute, 'dispctrl'):
+ warn('[3.10] _render_attribute prototype has changed, please'
+ ' update %s' % self.__class___, DeprecationWarning)
+ self._render_attribute(dispctrl, rschema, value, role=role,
+ table=True)
+ else:
warn('[3.6] _render_attribute prototype has changed, please'
' update %s' % self.__class___, DeprecationWarning)
self._render_attribute(rschema, value, role=role, table=True)
@@ -166,9 +172,14 @@
continue
rset = self._relation_rset(entity, rschema, role, dispctrl)
if rset:
- try:
+ if support_args(self._render_relation, 'label'):
+ label = self._rel_label(entity, rschema, role, dispctrl)
+ self._render_relation(label, dispctrl, rset, 'autolimited')
+ elif not support_args(self._render_relation, 'showlabel'):
+ warn('[3.10] _render_relation prototype has changed, '
+ 'please update %s' % self.__class__, DeprecationWarning)
self._render_relation(dispctrl, rset, 'autolimited')
- except TypeError:
+ else:
warn('[3.6] _render_relation prototype has changed, '
'please update %s' % self.__class__, DeprecationWarning)
self._render_relation(rset, dispctrl, 'autolimited',
@@ -205,7 +216,7 @@
rset = self._relation_rset(entity, rschema, role, dispctrl)
if not rset:
continue
- label = display_name(self._cw, rschema.type, role)
+ label = self._rel_label(entity, rschema, role, dispctrl)
vid = dispctrl.get('vid', 'sidebox')
sideboxes.append( (label, rset, vid, dispctrl) )
sideboxes += self._cw.vreg['boxes'].poss_visible_objects(
@@ -251,25 +262,16 @@
rset = dispctrl['filter'](rset)
return rset
- def _render_relation(self, dispctrl, rset, defaultvid):
+ def _render_relation(self, label, dispctrl, rset, defaultvid):
self.w(u'<div class="section">')
- if dispctrl.get('showlabel', self.show_rel_label):
- self.w(u'<h4>%s</h4>' % self._cw._(dispctrl['label']))
+ if label:
+ self.w(u'<h4>%s</h4>' % label)
self.wview(dispctrl.get('vid', defaultvid), rset,
initargs={'dispctrl': dispctrl})
self.w(u'</div>')
- def _render_attribute(self, dispctrl, rschema, value,
- role='subject', table=False):
- if rschema.final:
- showlabel = dispctrl.get('showlabel', self.show_attr_label)
- else:
- showlabel = dispctrl.get('showlabel', self.show_rel_label)
- if dispctrl.get('label'):
- label = self._cw._(dispctrl.get('label'))
- else:
- label = display_name(self._cw, rschema.type, role)
- self.field(label, value, show_label=showlabel, tr=False, table=table)
+ def _render_attribute(self, label, value, table=False):
+ self.field(label, value, tr=False, table=table)
def _rel_label(self, entity, rschema, role, dispctrl):
if rschema.final:
--- a/web/views/sessions.py Tue Aug 10 18:39:48 2010 +0200
+++ b/web/views/sessions.py Wed Aug 11 10:08:48 2010 +0200
@@ -17,8 +17,8 @@
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""web session component: by dfault the session is actually the db connection
object :/
+"""
-"""
__docformat__ = "restructuredtext en"
from cubicweb.web import InvalidSession
@@ -51,9 +51,6 @@
if not sessionid in self._sessions:
raise InvalidSession()
session = self._sessions[sessionid]
- if self.has_expired(session):
- self.close_session(session)
- raise InvalidSession()
try:
user = self.authmanager.validate_session(req, session)
except InvalidSession:
--- a/web/views/startup.py Tue Aug 10 18:39:48 2010 +0200
+++ b/web/views/startup.py Wed Aug 11 10:08:48 2010 +0200
@@ -159,15 +159,14 @@
url = self._cw.build_url(etype)
etypelink = u' <a href="%s">%s</a> (%d)' % (
xml_escape(url), label, nb)
- yield (label, etypelink, self.add_entity_link(eschema, req))
+ if eschema.has_perm(req, 'add'):
+ yield (label, etypelink, self.add_entity_link(etype))
- def add_entity_link(self, eschema, req):
- """creates a [+] link for adding an entity if user has permission to do so"""
- if not eschema.has_perm(req, 'add'):
- return u''
+ def add_entity_link(self, etype):
+ """creates a [+] link for adding an entity"""
+ url = self._cw.vreg["etypes"].etype_class(etype).cw_create_url(self._cw)
return u'[<a href="%s" title="%s">+</a>]' % (
- xml_escape(self.create_url(eschema.type)),
- self._cw.__('add a %s' % eschema))
+ xml_escape(url), self._cw.__('add a %s' % etype))
class IndexView(ManageView):
--- a/web/webconfig.py Tue Aug 10 18:39:48 2010 +0200
+++ b/web/webconfig.py Wed Aug 11 10:08:48 2010 +0200
@@ -135,17 +135,6 @@
"Should be 0 or greater than repository\'s session-time.",
'group': 'web', 'level': 2,
}),
- ('cleanup-session-time',
- {'type' : 'time',
- 'default': '24h',
- 'help': 'duration of inactivity after which a connection '
- 'will be closed, to limit memory consumption (avoid sessions that '
- 'never expire and cause memory leak when http-session-time is 0). '
- 'So even if http-session-time is 0 and the user don\'t close his '
- 'browser, he will have to reauthenticate after this time of '
- 'inactivity. Default to 24h.',
- 'group': 'web', 'level': 3,
- }),
('cleanup-anonymous-session-time',
{'type' : 'time',
'default': '5min',