rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
--- a/__init__.py Thu Aug 13 09:23:22 2009 +0200
+++ b/__init__.py Thu Aug 13 09:30:03 2009 +0200
@@ -7,7 +7,6 @@
:license: Library General Public License version 2 - http://www.gnu.org/licenses
"""
__docformat__ = "restructuredtext en"
-from cubicweb.__pkginfo__ import version as __version__
import __builtin__
# '_' is available in builtins to mark internationalized string but should
@@ -21,7 +20,6 @@
from StringIO import StringIO
from urllib import quote as urlquote, unquote as urlunquote
-from logilab.common.decorators import cached
from logilab.common.logging_ext import set_log_methods
if os.environ.get('APYCOT_ROOT'):
@@ -29,6 +27,8 @@
else:
logging.basicConfig()
+from cubicweb.__pkginfo__ import version as __version__
+
set_log_methods(sys.modules[__name__], logging.getLogger('cubicweb'))
@@ -57,173 +57,6 @@
StringIO.write(self, data)
-class RequestSessionMixIn(object):
- """mixin class containing stuff shared by server session and web request
- """
- def __init__(self, vreg):
- self.vreg = vreg
- try:
- encoding = vreg.property_value('ui.encoding')
- except: # no vreg or property not registered
- encoding = 'utf-8'
- self.encoding = encoding
- # cache result of execution for (rql expr / eids),
- # should be emptied on commit/rollback of the server session / web
- # connection
- self.local_perm_cache = {}
-
- def property_value(self, key):
- if self.user:
- return self.user.property_value(key)
- return self.vreg.property_value(key)
-
- def etype_rset(self, etype, size=1):
- """return a fake result set for a particular entity type"""
- from cubicweb.rset import ResultSet
- rset = ResultSet([('A',)]*size, '%s X' % etype,
- description=[(etype,)]*size)
- def get_entity(row, col=0, etype=etype, req=self, rset=rset):
- return req.vreg.etype_class(etype)(req, rset, row, col)
- rset.get_entity = get_entity
- return self.decorate_rset(rset)
-
- def eid_rset(self, eid, etype=None):
- """return a result set for the given eid without doing actual query
- (we have the eid, we can suppose it exists and user has access to the
- entity)
- """
- from cubicweb.rset import ResultSet
- eid = typed_eid(eid)
- if etype is None:
- etype = self.describe(eid)[0]
- rset = ResultSet([(eid,)], 'Any X WHERE X eid %(x)s', {'x': eid},
- [(etype,)])
- return self.decorate_rset(rset)
-
- def empty_rset(self):
- """return a result set for the given eid without doing actual query
- (we have the eid, we can suppose it exists and user has access to the
- entity)
- """
- from cubicweb.rset import ResultSet
- return self.decorate_rset(ResultSet([], 'Any X WHERE X eid -1'))
-
- def entity_from_eid(self, eid, etype=None):
- try:
- return self.entity_cache(eid)
- except KeyError:
- rset = self.eid_rset(eid, etype)
- entity = rset.get_entity(0, 0)
- self.set_entity_cache(entity)
- return entity
-
- def entity_cache(self, eid):
- raise KeyError
- def set_entity_cache(self, entity):
- pass
- # url generation methods ##################################################
-
- def build_url(self, *args, **kwargs):
- """return an absolute URL using params dictionary key/values as URL
- parameters. Values are automatically URL quoted, and the
- publishing method to use may be specified or will be guessed.
- """
- # use *args since we don't want first argument to be "anonymous" to
- # avoid potential clash with kwargs
- assert len(args) == 1, 'only 0 or 1 non-named-argument expected'
- method = args[0]
- base_url = kwargs.pop('base_url', None)
- if base_url is None:
- base_url = self.base_url()
- if '_restpath' in kwargs:
- assert method == 'view', method
- path = kwargs.pop('_restpath')
- else:
- path = method
- if not kwargs:
- return u'%s%s' % (base_url, path)
- return u'%s%s?%s' % (base_url, path, self.build_url_params(**kwargs))
-
-
- def build_url_params(self, **kwargs):
- """return encoded params to incorporate them in an URL"""
- args = []
- for param, values in kwargs.items():
- if not isinstance(values, (list, tuple)):
- values = (values,)
- for value in values:
- args.append(u'%s=%s' % (param, self.url_quote(value)))
- return '&'.join(args)
-
- def url_quote(self, value, safe=''):
- """urllib.quote is not unicode safe, use this method to do the
- necessary encoding / decoding. Also it's designed to quote each
- part of a url path and so the '/' character will be encoded as well.
- """
- if isinstance(value, unicode):
- quoted = urlquote(value.encode(self.encoding), safe=safe)
- return unicode(quoted, self.encoding)
- return urlquote(str(value), safe=safe)
-
- def url_unquote(self, quoted):
- """returns a unicode unquoted string
-
- decoding is based on `self.encoding` which is the encoding
- used in `url_quote`
- """
- if isinstance(quoted, unicode):
- quoted = quoted.encode(self.encoding)
- try:
- return unicode(urlunquote(quoted), self.encoding)
- except UnicodeDecodeError: # might occurs on manually typed URLs
- return unicode(urlunquote(quoted), 'iso-8859-1')
-
-
- # session's user related methods #####################################
-
- @cached
- def user_data(self):
- """returns a dictionnary with this user's information"""
- userinfo = {}
- if self.is_internal_session:
- userinfo['login'] = "cubicweb"
- userinfo['name'] = "cubicweb"
- userinfo['email'] = ""
- return userinfo
- user = self.actual_session().user
- rql = "Any F,S,A where U eid %(x)s, U firstname F, U surname S, U primary_email E, E address A"
- try:
- firstname, lastname, email = self.execute(rql, {'x': user.eid}, 'x')[0]
- if firstname is None and lastname is None:
- userinfo['name'] = ''
- else:
- userinfo['name'] = ("%s %s" % (firstname, lastname))
- userinfo['email'] = email
- except IndexError:
- userinfo['name'] = None
- userinfo['email'] = None
- userinfo['login'] = user.login
- return userinfo
-
- def is_internal_session(self):
- """overrided on the server-side"""
- return False
-
- # abstract methods to override according to the web front-end #############
-
- def base_url(self):
- """return the root url of the instance"""
- raise NotImplementedError
-
- def decorate_rset(self, rset):
- """add vreg/req (at least) attributes to the given result set """
- raise NotImplementedError
-
- def describe(self, eid):
- """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
- raise NotImplementedError
-
-
# XXX 2.45 is allowing nicer entity type names, use this map for bw compat
ETYPE_NAME_MAP = {# 3.2 migration
'ECache': 'CWCache',
--- a/appobject.py Thu Aug 13 09:23:22 2009 +0200
+++ b/appobject.py Thu Aug 13 09:30:03 2009 +0200
@@ -11,17 +11,14 @@
import types
from logging import getLogger
-from datetime import datetime, timedelta, time
+from datetime import datetime, timedelta
from logilab.common.decorators import classproperty
from logilab.common.deprecation import deprecated
from logilab.common.logging_ext import set_log_methods
-from rql.nodes import VariableRef, SubQuery
-from rql.stmts import Union, Select
-
from cubicweb import Unauthorized, NoSelectableObject
-from cubicweb.utils import UStringIO, ustrftime, strptime, todate, todatetime
+from cubicweb.utils import UStringIO
ONESECOND = timedelta(0, 1, 0)
CACHE_REGISTRY = {}
@@ -293,8 +290,6 @@
"""
cls.build___select__()
cls.vreg = registry.vreg
- cls.schema = registry.schema
- cls.config = registry.config
cls.register_properties()
return cls
@@ -302,7 +297,7 @@
def vreg_initialization_completed(cls):
pass
- # Eproperties definition:
+ # properties definition:
# key: id of the property (the actual CWProperty key is build using
# <registry name>.<obj id>.<property id>
# value: tuple (property type, vocabfunc, default value, property description)
@@ -326,15 +321,6 @@
def propkey(cls, propid):
return '%s.%s.%s' % (cls.__registry__, cls.id, propid)
- @classproperty
- @deprecated('[3.4] use __select__ and & or | operators')
- def __selectors__(cls):
- selector = cls.__select__
- if isinstance(selector, AndSelector):
- return tuple(selector.selectors)
- if not isinstance(selector, tuple):
- selector = (selector,)
- return selector
def __init__(self, req=None, rset=None, row=None, col=None, **extra):
super(AppObject, self).__init__()
@@ -369,64 +355,12 @@
assert self.req
return self.req.property_value(self.propkey(propid))
- def limited_rql(self):
- """return a printable rql for the result set associated to the object,
- with limit/offset correctly set according to maximum page size and
- currently displayed page when necessary
- """
- # try to get page boundaries from the navigation component
- # XXX we should probably not have a ref to this component here (eg in
- # cubicweb.common)
- nav = self.vreg['components'].select_or_none('navigation', self.req,
- rset=self.rset)
- if nav:
- start, stop = nav.page_boundaries()
- rql = self._limit_offset_rql(stop - start, start)
- # result set may have be limited manually in which case navigation won't
- # apply
- elif self.rset.limited:
- rql = self._limit_offset_rql(*self.rset.limited)
- # navigation component doesn't apply and rset has not been limited, no
- # need to limit query
- else:
- rql = self.rset.printable_rql()
- return rql
-
- def _limit_offset_rql(self, limit, offset):
- rqlst = self.rset.syntax_tree()
- if len(rqlst.children) == 1:
- select = rqlst.children[0]
- olimit, ooffset = select.limit, select.offset
- select.limit, select.offset = limit, offset
- rql = rqlst.as_string(kwargs=self.rset.args)
- # restore original limit/offset
- select.limit, select.offset = olimit, ooffset
- else:
- newselect = Select()
- newselect.limit = limit
- newselect.offset = offset
- aliases = [VariableRef(newselect.get_variable(vref.name, i))
- for i, vref in enumerate(rqlst.selection)]
- newselect.set_with([SubQuery(aliases, rqlst)], check=False)
- newunion = Union()
- newunion.append(newselect)
- rql = rqlst.as_string(kwargs=self.rset.args)
- rqlst.parent = None
- return rql
-
def view(self, __vid, rset=None, __fallback_oid=None, __registry='views',
**kwargs):
"""shortcut to self.vreg.view method avoiding to pass self.req"""
return self.vreg[__registry].render(__vid, self.req, __fallback_oid,
rset=rset, **kwargs)
- def initialize_varmaker(self):
- varmaker = self.req.get_page_data('rql_varmaker')
- if varmaker is None:
- varmaker = self.req.varmaker
- self.req.set_page_data('rql_varmaker', varmaker)
- self.varmaker = varmaker
-
# url generation methods ##################################################
controller = 'view'
@@ -452,42 +386,6 @@
method = self.req.relative_path(includeparams=False) or 'view'
return self.req.build_url(method, **kwargs)
- # various resources accessors #############################################
-
- @deprecated('[3.5] use self.rset.get_entity(row,col) instead')
- def entity(self, row, col=0):
- """short cut to get an entity instance for a particular row/column
- (col default to 0)
- """
- return self.rset.get_entity(row, col)
-
- def complete_entity(self, row, col=0, skip_bytes=True):
- """short cut to get an completed entity instance for a particular
- row (all instance's attributes have been fetched)
- """
- entity = self.rset.get_entity(row, col)
- entity.complete(skip_bytes=skip_bytes)
- return entity
-
- def user_rql_callback(self, args, msg=None):
- """register a user callback to execute some rql query and return an url
- to call it ready to be inserted in html
- """
- def rqlexec(req, rql, args=None, key=None):
- req.execute(rql, args, key)
- return self.user_callback(rqlexec, args, msg)
-
- def user_callback(self, cb, args, msg=None, nonify=False):
- """register the given user callback and return an url to call it ready to be
- inserted in html
- """
- from simplejson import dumps
- self.req.add_js('cubicweb.ajax.js')
- cbname = self.req.register_onetime_callback(cb, *args)
- msg = dumps(msg or '')
- return "javascript:userCallbackThenReloadPage('%s', %s)" % (
- cbname, msg)
-
# formating methods #######################################################
def tal_render(self, template, variables):
@@ -503,70 +401,66 @@
template.expand(context, output)
return output.getvalue()
+ # deprecated ###############################################################
+
+ @classproperty
+ @deprecated('[3.4] use __select__ and & or | operators')
+ def __selectors__(cls):
+ selector = cls.__select__
+ if isinstance(selector, AndSelector):
+ return tuple(selector.selectors)
+ if not isinstance(selector, tuple):
+ selector = (selector,)
+ return selector
+
+ @classmethod
+ @deprecated('[3.5] use vreg.schema')
+ def schema(cls):
+ return cls.vreg.schema
+
+ @classmethod
+ @deprecated('[3.5] use vreg.config')
+ def schema(cls):
+ return cls.vreg.config
+
+ @deprecated('[3.5] use req.varmaker')
+ def initialize_varmaker(self):
+ self.varmaker = self.req.varmaker
+
+ @deprecated('[3.5] use rset.limited_rql')
+ def limited_rql(self):
+ return self.rset.limited_rql()
+
+ @deprecated('[3.5] use self.rset.complete_entity(row,col) instead')
+ def complete_entity(self, row, col=0, skip_bytes=True):
+ return self.rset.complete_entity(row, col, skip_bytes)
+
+ @deprecated('[3.5] use self.rset.get_entity(row,col) instead')
+ def entity(self, row, col=0):
+ return self.rset.get_entity(row, col)
+
+ @deprecated('[3.5] use req.user_rql_callback')
+ def user_rql_callback(self, args, msg=None):
+ return self.req.user_rql_callback(args, msg)
+
+ @deprecated('[3.5] use req.user_callback')
+ def user_callback(self, cb, args, msg=None, nonify=False):
+ return self.req.user_callback(cb, args, msg, nonify)
+
+ @deprecated('[3.5] use req.format_date')
def format_date(self, date, date_format=None, time=False):
- """return a string for a date time according to instance's
- configuration
- """
- if date:
- if date_format is None:
- if time:
- date_format = self.req.property_value('ui.datetime-format')
- else:
- date_format = self.req.property_value('ui.date-format')
- return ustrftime(date, date_format)
- return u''
+ return self.req.format_date(date, date_format, time)
+ @deprecated('[3.5] use req.format_timoe')
def format_time(self, time):
- """return a string for a time according to instance's
- configuration
- """
- if time:
- return ustrftime(time, self.req.property_value('ui.time-format'))
- return u''
+ return self.req.format_time(time)
+ @deprecated('[3.5] use req.format_float')
def format_float(self, num):
- """return a string for floating point number according to instance's
- configuration
- """
- if num:
- return self.req.property_value('ui.float-format') % num
- return u''
+ return self.req.format_float(num)
+ @deprecated('[3.5] use req.parse_datetime')
def parse_datetime(self, value, etype='Datetime'):
- """get a datetime or time from a string (according to etype)
- Datetime formatted as Date are accepted
- """
- assert etype in ('Datetime', 'Date', 'Time'), etype
- # XXX raise proper validation error
- if etype == 'Datetime':
- format = self.req.property_value('ui.datetime-format')
- try:
- return todatetime(strptime(value, format))
- except ValueError:
- pass
- elif etype == 'Time':
- format = self.req.property_value('ui.time-format')
- try:
- # (adim) I can't find a way to parse a Time with a custom format
- date = strptime(value, format) # this returns a DateTime
- return time(date.hour, date.minute, date.second)
- except ValueError:
- raise ValueError('can\'t parse %r (expected %s)' % (value, format))
- try:
- format = self.req.property_value('ui.date-format')
- dt = strptime(value, format)
- if etype == 'Datetime':
- return todatetime(dt)
- return todate(dt)
- except ValueError:
- raise ValueError('can\'t parse %r (expected %s)' % (value, format))
-
- # security related methods ################################################
-
- def ensure_ro_rql(self, rql):
- """raise an exception if the given rql is not a select query"""
- first = rql.split(' ', 1)[0].lower()
- if first in ('insert', 'set', 'delete'):
- raise Unauthorized(self.req._('only select queries are authorized'))
+ return self.req.parse_datetime(value, etype)
set_log_methods(AppObject, getLogger('cubicweb.appobject'))
--- a/dbapi.py Thu Aug 13 09:23:22 2009 +0200
+++ b/dbapi.py Thu Aug 13 09:30:03 2009 +0200
@@ -19,8 +19,9 @@
from logilab.common.decorators import monkeypatch
from logilab.common.deprecation import deprecated
-from cubicweb import ETYPE_NAME_MAP, ConnectionError, RequestSessionMixIn
-from cubicweb import cwvreg, cwconfig
+from cubicweb import ETYPE_NAME_MAP, ConnectionError, cwvreg, cwconfig
+from cubicweb.req import RequestSessionBase
+
_MARKER = object()
@@ -174,7 +175,7 @@
return repo, cnx
-class DBAPIRequest(RequestSessionMixIn):
+class DBAPIRequest(RequestSessionBase):
def __init__(self, vreg, cnx=None):
super(DBAPIRequest, self).__init__(vreg)
--- a/devtools/fake.py Thu Aug 13 09:23:22 2009 +0200
+++ b/devtools/fake.py Thu Aug 13 09:30:03 2009 +0200
@@ -11,7 +11,7 @@
from indexer import get_indexer
-from cubicweb import RequestSessionMixIn
+from cubicweb.req import RequestSessionBase
from cubicweb.web.request import CubicWebRequestBase
from cubicweb.devtools import BASE_URL, BaseApptestConfiguration
@@ -173,7 +173,7 @@
return True
-class FakeSession(RequestSessionMixIn):
+class FakeSession(RequestSessionBase):
def __init__(self, repo=None, user=None):
self.repo = repo
self.vreg = getattr(self.repo, 'vreg', FakeVReg())
--- a/goa/db.py Thu Aug 13 09:23:22 2009 +0200
+++ b/goa/db.py Thu Aug 13 09:30:03 2009 +0200
@@ -35,7 +35,8 @@
from logilab.common.decorators import cached, iclassmethod
-from cubicweb import RequestSessionMixIn, Binary, entities
+from cubicweb import Binary, entities
+from cubicweb.req import RequestSessionBase
from cubicweb.rset import ResultSet
from cubicweb.entity import metaentity
from cubicweb.server.utils import crypt_password
@@ -92,7 +93,7 @@
def needrequest(wrapped):
def wrapper(cls, *args, **kwargs):
req = kwargs.pop('req', None)
- if req is None and args and isinstance(args[0], RequestSessionMixIn):
+ if req is None and args and isinstance(args[0], RequestSessionBase):
args = list(args)
req = args.pop(0)
if req is None:
@@ -155,7 +156,7 @@
#
# Entity prototype:
# __init__(self, req, rset, row=None, col=0)
- if args and isinstance(args[0], RequestSessionMixIn) or 'req' in kwargs:
+ if args and isinstance(args[0], RequestSessionBase) or 'req' in kwargs:
super(Model, self).__init__(*args, **kwargs)
self._gaeinitargs = None
else:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/req.py Thu Aug 13 09:30:03 2009 +0200
@@ -0,0 +1,251 @@
+"""Base class for request/session
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: Library General Public License version 2 - http://www.gnu.org/licenses
+"""
+__docformat__ = "restructuredtext en"
+
+from datetime import time
+
+from logilab.common.decorators import cached
+
+from cubicweb import Unauthorized, typed_eid
+from cubicweb.rset import ResultSet
+from cubicweb.utils import ustrftime, strptime, todate, todatetime
+
+class RequestSessionBase(object):
+ """base class containing stuff shared by server session and web request
+ """
+ def __init__(self, vreg):
+ self.vreg = vreg
+ try:
+ encoding = vreg.property_value('ui.encoding')
+ except: # no vreg or property not registered
+ encoding = 'utf-8'
+ self.encoding = encoding
+ # cache result of execution for (rql expr / eids),
+ # should be emptied on commit/rollback of the server session / web
+ # connection
+ self.local_perm_cache = {}
+ self._ = unicode
+
+ def property_value(self, key):
+ """return value of the property with the given key, giving priority to
+ user specific value if any, else using site value
+ """
+ if self.user:
+ return self.user.property_value(key)
+ return self.vreg.property_value(key)
+
+ def etype_rset(self, etype, size=1):
+ """return a fake result set for a particular entity type"""
+ rset = ResultSet([('A',)]*size, '%s X' % etype,
+ description=[(etype,)]*size)
+ def get_entity(row, col=0, etype=etype, req=self, rset=rset):
+ return req.vreg.etype_class(etype)(req, rset, row, col)
+ rset.get_entity = get_entity
+ return self.decorate_rset(rset)
+
+ def eid_rset(self, eid, etype=None):
+ """return a result set for the given eid without doing actual query
+ (we have the eid, we can suppose it exists and user has access to the
+ entity)
+ """
+ eid = typed_eid(eid)
+ if etype is None:
+ etype = self.describe(eid)[0]
+ rset = ResultSet([(eid,)], 'Any X WHERE X eid %(x)s', {'x': eid},
+ [(etype,)])
+ return self.decorate_rset(rset)
+
+ def empty_rset(self):
+ """return a result set for the given eid without doing actual query
+ (we have the eid, we can suppose it exists and user has access to the
+ entity)
+ """
+ return self.decorate_rset(ResultSet([], 'Any X WHERE X eid -1'))
+
+ def entity_from_eid(self, eid, etype=None):
+ """return an entity instance for the given eid. No query is done"""
+ try:
+ return self.entity_cache(eid)
+ except KeyError:
+ rset = self.eid_rset(eid, etype)
+ entity = rset.get_entity(0, 0)
+ self.set_entity_cache(entity)
+ return entity
+
+ def entity_cache(self, eid):
+ raise KeyError
+
+ def set_entity_cache(self, entity):
+ pass
+
+ def ensure_ro_rql(self, rql):
+ """raise an exception if the given rql is not a select query"""
+ first = rql.split(' ', 1)[0].lower()
+ if first in ('insert', 'set', 'delete'):
+ raise Unauthorized(self._('only select queries are authorized'))
+
+ # url generation methods ##################################################
+
+ def build_url(self, *args, **kwargs):
+ """return an absolute URL using params dictionary key/values as URL
+ parameters. Values are automatically URL quoted, and the
+ publishing method to use may be specified or will be guessed.
+ """
+ # use *args since we don't want first argument to be "anonymous" to
+ # avoid potential clash with kwargs
+ assert len(args) == 1, 'only 0 or 1 non-named-argument expected'
+ method = args[0]
+ base_url = kwargs.pop('base_url', None)
+ if base_url is None:
+ base_url = self.base_url()
+ if '_restpath' in kwargs:
+ assert method == 'view', method
+ path = kwargs.pop('_restpath')
+ else:
+ path = method
+ if not kwargs:
+ return u'%s%s' % (base_url, path)
+ return u'%s%s?%s' % (base_url, path, self.build_url_params(**kwargs))
+
+
+ def build_url_params(self, **kwargs):
+ """return encoded params to incorporate them in an URL"""
+ args = []
+ for param, values in kwargs.items():
+ if not isinstance(values, (list, tuple)):
+ values = (values,)
+ for value in values:
+ args.append(u'%s=%s' % (param, self.url_quote(value)))
+ return '&'.join(args)
+
+ def url_quote(self, value, safe=''):
+ """urllib.quote is not unicode safe, use this method to do the
+ necessary encoding / decoding. Also it's designed to quote each
+ part of a url path and so the '/' character will be encoded as well.
+ """
+ if isinstance(value, unicode):
+ quoted = urlquote(value.encode(self.encoding), safe=safe)
+ return unicode(quoted, self.encoding)
+ return urlquote(str(value), safe=safe)
+
+ def url_unquote(self, quoted):
+ """returns a unicode unquoted string
+
+ decoding is based on `self.encoding` which is the encoding
+ used in `url_quote`
+ """
+ if isinstance(quoted, unicode):
+ quoted = quoted.encode(self.encoding)
+ try:
+ return unicode(urlunquote(quoted), self.encoding)
+ except UnicodeDecodeError: # might occurs on manually typed URLs
+ return unicode(urlunquote(quoted), 'iso-8859-1')
+
+ # bound user related methods ###############################################
+
+ @cached
+ def user_data(self):
+ """returns a dictionnary with this user's information"""
+ userinfo = {}
+ if self.is_internal_session:
+ userinfo['login'] = "cubicweb"
+ userinfo['name'] = "cubicweb"
+ userinfo['email'] = ""
+ return userinfo
+ user = self.actual_session().user
+ rql = "Any F,S,A where U eid %(x)s, U firstname F, U surname S, U primary_email E, E address A"
+ try:
+ firstname, lastname, email = self.execute(rql, {'x': user.eid}, 'x')[0]
+ if firstname is None and lastname is None:
+ userinfo['name'] = ''
+ else:
+ userinfo['name'] = ("%s %s" % (firstname, lastname))
+ userinfo['email'] = email
+ except IndexError:
+ userinfo['name'] = None
+ userinfo['email'] = None
+ userinfo['login'] = user.login
+ return userinfo
+
+ def is_internal_session(self):
+ """overrided on the server-side"""
+ return False
+
+ # formating methods #######################################################
+
+ def format_date(self, date, date_format=None, time=False):
+ """return a string for a date time according to instance's
+ configuration
+ """
+ if date:
+ if date_format is None:
+ if time:
+ date_format = self.property_value('ui.datetime-format')
+ else:
+ date_format = self.property_value('ui.date-format')
+ return ustrftime(date, date_format)
+ return u''
+
+ def format_time(self, time):
+ """return a string for a time according to instance's
+ configuration
+ """
+ if time:
+ return ustrftime(time, self.property_value('ui.time-format'))
+ return u''
+
+ def format_float(self, num):
+ """return a string for floating point number according to instance's
+ configuration
+ """
+ if num:
+ return self.property_value('ui.float-format') % num
+ return u''
+
+ def parse_datetime(self, value, etype='Datetime'):
+ """get a datetime or time from a string (according to etype)
+ Datetime formatted as Date are accepted
+ """
+ assert etype in ('Datetime', 'Date', 'Time'), etype
+ # XXX raise proper validation error
+ if etype == 'Datetime':
+ format = self.property_value('ui.datetime-format')
+ try:
+ return todatetime(strptime(value, format))
+ except ValueError:
+ pass
+ elif etype == 'Time':
+ format = self.property_value('ui.time-format')
+ try:
+ # (adim) I can't find a way to parse a Time with a custom format
+ date = strptime(value, format) # this returns a DateTime
+ return time(date.hour, date.minute, date.second)
+ except ValueError:
+ raise ValueError('can\'t parse %r (expected %s)' % (value, format))
+ try:
+ format = self.property_value('ui.date-format')
+ dt = strptime(value, format)
+ if etype == 'Datetime':
+ return todatetime(dt)
+ return todate(dt)
+ except ValueError:
+ raise ValueError('can\'t parse %r (expected %s)' % (value, format))
+
+ # abstract methods to override according to the web front-end #############
+
+ def base_url(self):
+ """return the root url of the instance"""
+ raise NotImplementedError
+
+ def decorate_rset(self, rset):
+ """add vreg/req (at least) attributes to the given result set """
+ raise NotImplementedError
+
+ def describe(self, eid):
+ """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
+ raise NotImplementedError
--- a/rset.py Thu Aug 13 09:23:22 2009 +0200
+++ b/rset.py Thu Aug 13 09:30:03 2009 +0200
@@ -9,7 +9,7 @@
from logilab.common.decorators import cached, clear_cache, copy_cache
-from rql import nodes
+from rql import nodes, stmts
from cubicweb import NotAnEntity
@@ -240,8 +240,6 @@
rset = mapping[key]
rset.rows.append(self.rows[idx])
rset.description.append(self.description[idx])
-
-
for rset in result:
rset.rowcount = len(rset.rows)
if return_dict:
@@ -249,6 +247,51 @@
else:
return result
+ def limited_rql(self):
+ """return a printable rql for the result set associated to the object,
+ with limit/offset correctly set according to maximum page size and
+ currently displayed page when necessary
+ """
+ # try to get page boundaries from the navigation component
+ # XXX we should probably not have a ref to this component here (eg in
+ # cubicweb.common)
+ nav = self.vreg['components'].select_or_none('navigation', self.req,
+ rset=self)
+ if nav:
+ start, stop = nav.page_boundaries()
+ rql = self._limit_offset_rql(stop - start, start)
+ # result set may have be limited manually in which case navigation won't
+ # apply
+ elif self.limited:
+ rql = self._limit_offset_rql(*self.limited)
+ # navigation component doesn't apply and rset has not been limited, no
+ # need to limit query
+ else:
+ rql = self.printable_rql()
+ return rql
+
+ def _limit_offset_rql(self, limit, offset):
+ rqlst = self.syntax_tree()
+ if len(rqlst.children) == 1:
+ select = rqlst.children[0]
+ olimit, ooffset = select.limit, select.offset
+ select.limit, select.offset = limit, offset
+ rql = rqlst.as_string(kwargs=self.args)
+ # restore original limit/offset
+ select.limit, select.offset = olimit, ooffset
+ else:
+ newselect = stmts.Select()
+ newselect.limit = limit
+ newselect.offset = offset
+ aliases = [nodes.VariableRef(newselect.get_variable(vref.name, i))
+ for i, vref in enumerate(rqlst.selection)]
+ newselect.set_with([nodes.SubQuery(aliases, rqlst)], check=False)
+ newunion = stmts.Union()
+ newunion.append(newselect)
+ rql = rqlst.as_string(kwargs=self.args)
+ rqlst.parent = None
+ return rql
+
def limit(self, limit, offset=0, inplace=False):
"""limit the result set to the given number of rows optionaly starting
from an index different than 0
@@ -318,6 +361,14 @@
if self.rows[i][col] is not None:
yield self.get_entity(i, col)
+ def complete_entity(self, row, col=0, skip_bytes=True):
+ """short cut to get an completed entity instance for a particular
+ row (all instance's attributes have been fetched)
+ """
+ entity = self.get_entity(row, col)
+ entity.complete(skip_bytes=skip_bytes)
+ return entity
+
@cached
def get_entity(self, row, col=None):
"""special method for query retreiving a single entity, returns a
--- a/server/session.py Thu Aug 13 09:23:22 2009 +0200
+++ b/server/session.py Thu Aug 13 09:30:03 2009 +0200
@@ -15,7 +15,8 @@
from rql.nodes import VariableRef, Function, ETYPE_PYOBJ_MAP, etype_from_pyobj
from yams import BASE_TYPES
-from cubicweb import RequestSessionMixIn, Binary, UnknownEid
+from cubicweb import Binary, UnknownEid
+from cubicweb.req import RequestSessionBase
from cubicweb.dbapi import ConnectionProperties
from cubicweb.utils import make_uid
from cubicweb.server.rqlrewrite import RQLRewriter
@@ -41,7 +42,7 @@
return description
-class Session(RequestSessionMixIn):
+class Session(RequestSessionBase):
"""tie session id, user, connections pool and other session data all
together
"""
--- a/web/controller.py Thu Aug 13 09:23:22 2009 +0200
+++ b/web/controller.py Thu Aug 13 09:30:03 2009 +0200
@@ -90,7 +90,7 @@
# XXX assigning to self really necessary?
self.rset = None
if rql:
- self.ensure_ro_rql(rql)
+ self.req.ensure_ro_rql(rql)
if not isinstance(rql, unicode):
rql = unicode(rql, self.req.encoding)
pp = self.vreg['components'].select_or_none('magicsearch', self.req)
--- a/web/request.py Thu Aug 13 09:23:22 2009 +0200
+++ b/web/request.py Thu Aug 13 09:30:03 2009 +0200
@@ -15,11 +15,12 @@
from urlparse import urlsplit
from itertools import count
+from simplejson import dumps
+
from rql.utils import rqlvar_maker
from logilab.common.decorators import cached
from logilab.common.deprecation import deprecated
-
from logilab.mtconverter import xml_escape
from cubicweb.dbapi import DBAPIRequest
@@ -85,9 +86,16 @@
self.next_tabindex = self.tabindexgen.next
# page id, set by htmlheader template
self.pageid = None
- self.varmaker = rqlvar_maker()
self.datadir_url = self._datadir_url()
+ @property
+ def varmaker(self)
+ varmaker = self.get_page_data('rql_varmaker')
+ if varmaker is None:
+ varmaker = rqlvar_maker()
+ self.set_page_data('rql_varmaker', varmaker)
+ return varmaker
+
def set_connection(self, cnx, user=None):
"""method called by the session handler when the user is authenticated
or an anonymous connection is open
@@ -257,6 +265,24 @@
return breadcrumbs.pop()
return self.base_url()
+ def user_rql_callback(self, args, msg=None):
+ """register a user callback to execute some rql query and return an url
+ to call it ready to be inserted in html
+ """
+ def rqlexec(req, rql, args=None, key=None):
+ req.execute(rql, args, key)
+ return self.user_callback(rqlexec, args, msg)
+
+ def user_callback(self, cb, args, msg=None, nonify=False):
+ """register the given user callback and return an url to call it ready to be
+ inserted in html
+ """
+ self.add_js('cubicweb.ajax.js')
+ cbname = self.register_onetime_callback(cb, *args)
+ msg = dumps(msg or '')
+ return "javascript:userCallbackThenReloadPage('%s', %s)" % (
+ cbname, msg)
+
def register_onetime_callback(self, func, *args):
cbname = 'cb_%s' % (
sha.sha('%s%s%s%s' % (time.time(), func.__name__,
--- a/web/views/basecontrollers.py Thu Aug 13 09:23:22 2009 +0200
+++ b/web/views/basecontrollers.py Thu Aug 13 09:30:03 2009 +0200
@@ -290,7 +290,7 @@
def _exec(self, rql, args=None, eidkey=None, rocheck=True):
"""json mode: execute RQL and return resultset as json"""
if rocheck:
- self.ensure_ro_rql(rql)
+ self.req.ensure_ro_rql(rql)
try:
return self.req.execute(rql, args, eidkey)
except Exception, ex:
--- a/web/views/tableview.py Thu Aug 13 09:23:22 2009 +0200
+++ b/web/views/tableview.py Thu Aug 13 09:30:03 2009 +0200
@@ -293,7 +293,7 @@
displaycols=None, displayactions=None, mainindex=None):
"""Dumps a table displaying a composite query"""
actrql = self.req.form['actualrql']
- self.ensure_ro_rql(actrql)
+ self.req.ensure_ro_rql(actrql)
displaycols = self.displaycols(displaycols)
if displayactions is None and 'displayactions' in self.req.form:
displayactions = True