appobject.py
branchtls-sprint
changeset 1544 d8fb60c56d69
parent 1524 1d7575f5deaf
child 1635 866563e2d0fc
equal deleted inserted replaced
1543:dca9817bb337 1544:d8fb60c56d69
    21 from cubicweb.selectors import yes
    21 from cubicweb.selectors import yes
    22 from cubicweb.utils import UStringIO, ustrftime
    22 from cubicweb.utils import UStringIO, ustrftime
    23 
    23 
    24 ONESECOND = timedelta(0, 1, 0)
    24 ONESECOND = timedelta(0, 1, 0)
    25 
    25 
    26 class Cache(dict):    
    26 class Cache(dict):
    27     def __init__(self):
    27     def __init__(self):
    28         super(Cache, self).__init__()
    28         super(Cache, self).__init__()
    29         self.cache_creation_date = None
    29         self.cache_creation_date = None
    30         self.latest_cache_lookup = datetime.now()
    30         self.latest_cache_lookup = datetime.now()
    31     
    31 
    32 CACHE_REGISTRY = {}
    32 CACHE_REGISTRY = {}
    33 
    33 
    34 class AppRsetObject(VObject):
    34 class AppRsetObject(VObject):
    35     """This is the base class for CubicWeb application objects
    35     """This is the base class for CubicWeb application objects
    36     which are selected according to a request and result set.
    36     which are selected according to a request and result set.
    37     
    37 
    38     Classes are kept in the vregistry and instantiation is done at selection
    38     Classes are kept in the vregistry and instantiation is done at selection
    39     time.
    39     time.
    40     
    40 
    41     At registration time, the following attributes are set on the class:
    41     At registration time, the following attributes are set on the class:
    42     :vreg:
    42     :vreg:
    43       the application's registry
    43       the application's registry
    44     :schema:
    44     :schema:
    45       the application's schema
    45       the application's schema
    60         cls.vreg = vreg
    60         cls.vreg = vreg
    61         cls.schema = vreg.schema
    61         cls.schema = vreg.schema
    62         cls.config = vreg.config
    62         cls.config = vreg.config
    63         cls.register_properties()
    63         cls.register_properties()
    64         return cls
    64         return cls
    65     
    65 
    66     @classmethod
    66     @classmethod
    67     def vreg_initialization_completed(cls):
    67     def vreg_initialization_completed(cls):
    68         pass
    68         pass
    69     
    69 
    70     @classmethod
    70     @classmethod
    71     def selected(cls, *args, **kwargs):
    71     def selected(cls, *args, **kwargs):
    72         """by default web app objects are usually instantiated on
    72         """by default web app objects are usually instantiated on
    73         selection according to a request, a result set, and optional
    73         selection according to a request, a result set, and optional
    74         row and col
    74         row and col
    83     #        possible types are those used by `logilab.common.configuration`
    83     #        possible types are those used by `logilab.common.configuration`
    84     #
    84     #
    85     # notice that when it exists multiple objects with the same id (adaptation,
    85     # notice that when it exists multiple objects with the same id (adaptation,
    86     # overriding) only the first encountered definition is considered, so those
    86     # overriding) only the first encountered definition is considered, so those
    87     # objects can't try to have different default values for instance.
    87     # objects can't try to have different default values for instance.
    88     
    88 
    89     property_defs = {}
    89     property_defs = {}
    90     
    90 
    91     @classmethod
    91     @classmethod
    92     def register_properties(cls):
    92     def register_properties(cls):
    93         for propid, pdef in cls.property_defs.items():
    93         for propid, pdef in cls.property_defs.items():
    94             pdef = pdef.copy() # may be shared
    94             pdef = pdef.copy() # may be shared
    95             pdef['default'] = getattr(cls, propid, pdef['default'])
    95             pdef['default'] = getattr(cls, propid, pdef['default'])
    96             pdef['sitewide'] = getattr(cls, 'site_wide', pdef.get('sitewide'))
    96             pdef['sitewide'] = getattr(cls, 'site_wide', pdef.get('sitewide'))
    97             cls.vreg.register_property(cls.propkey(propid), **pdef)
    97             cls.vreg.register_property(cls.propkey(propid), **pdef)
    98         
    98 
    99     @classmethod
    99     @classmethod
   100     def propkey(cls, propid):
   100     def propkey(cls, propid):
   101         return '%s.%s.%s' % (cls.__registry__, cls.id, propid)
   101         return '%s.%s.%s' % (cls.__registry__, cls.id, propid)
   102 
   102 
   103     @classproperty
   103     @classproperty
   107         if isinstance(selector, AndSelector):
   107         if isinstance(selector, AndSelector):
   108             return tuple(selector.selectors)
   108             return tuple(selector.selectors)
   109         if not isinstance(selector, tuple):
   109         if not isinstance(selector, tuple):
   110             selector = (selector,)
   110             selector = (selector,)
   111         return selector
   111         return selector
   112     
   112 
   113     def __init__(self, req=None, rset=None, row=None, col=None, **extra):
   113     def __init__(self, req=None, rset=None, row=None, col=None, **extra):
   114         super(AppRsetObject, self).__init__()
   114         super(AppRsetObject, self).__init__()
   115         self.req = req
   115         self.req = req
   116         self.rset = rset
   116         self.rset = rset
   117         self.row = row
   117         self.row = row
   118         self.col = col
   118         self.col = col
   119         self.extra_kwargs = extra
   119         self.extra_kwargs = extra
   120         
   120 
   121     def get_cache(self, cachename):
   121     def get_cache(self, cachename):
   122         """
   122         """
   123         NOTE: cachename should be dotted names as in :
   123         NOTE: cachename should be dotted names as in :
   124         - cubicweb.mycache
   124         - cubicweb.mycache
   125         - cubes.blog.mycache 
   125         - cubes.blog.mycache
   126         - etc.
   126         - etc.
   127         """
   127         """
   128         if cachename in CACHE_REGISTRY:
   128         if cachename in CACHE_REGISTRY:
   129             cache = CACHE_REGISTRY[cachename]
   129             cache = CACHE_REGISTRY[cachename]
   130         else:
   130         else:
   131             cache = Cache()
   131             cache = Cache()
   132             CACHE_REGISTRY[cachename] = cache
   132             CACHE_REGISTRY[cachename] = cache
   133         _now = datetime.now()
   133         _now = datetime.now()
   134         if _now > cache.latest_cache_lookup + ONESECOND:
   134         if _now > cache.latest_cache_lookup + ONESECOND:
   135             ecache = self.req.execute('Any C,T WHERE C is CWCache, C name %(name)s, C timestamp T', 
   135             ecache = self.req.execute('Any C,T WHERE C is CWCache, C name %(name)s, C timestamp T',
   136                                       {'name':cachename}).get_entity(0,0)
   136                                       {'name':cachename}).get_entity(0,0)
   137             cache.latest_cache_lookup = _now
   137             cache.latest_cache_lookup = _now
   138             if not ecache.valid(cache.cache_creation_date):
   138             if not ecache.valid(cache.cache_creation_date):
   139                 cache.clear()
   139                 cache.clear()
   140                 cache.cache_creation_date = _now
   140                 cache.cache_creation_date = _now
   141         return cache
   141         return cache
   142 
   142 
   143     def propval(self, propid):
   143     def propval(self, propid):
   144         assert self.req
   144         assert self.req
   145         return self.req.property_value(self.propkey(propid))
   145         return self.req.property_value(self.propkey(propid))
   146     
   146 
   147     def limited_rql(self):
   147     def limited_rql(self):
   148         """return a printable rql for the result set associated to the object,
   148         """return a printable rql for the result set associated to the object,
   149         with limit/offset correctly set according to maximum page size and
   149         with limit/offset correctly set according to maximum page size and
   150         currently displayed page when necessary
   150         currently displayed page when necessary
   151         """
   151         """
   163         # navigation component doesn't apply and rset has not been limited, no
   163         # navigation component doesn't apply and rset has not been limited, no
   164         # need to limit query
   164         # need to limit query
   165         else:
   165         else:
   166             rql = self.rset.printable_rql()
   166             rql = self.rset.printable_rql()
   167         return rql
   167         return rql
   168     
   168 
   169     def _limit_offset_rql(self, limit, offset):
   169     def _limit_offset_rql(self, limit, offset):
   170         rqlst = self.rset.syntax_tree()
   170         rqlst = self.rset.syntax_tree()
   171         if len(rqlst.children) == 1:
   171         if len(rqlst.children) == 1:
   172             select = rqlst.children[0]
   172             select = rqlst.children[0]
   173             olimit, ooffset = select.limit, select.offset
   173             olimit, ooffset = select.limit, select.offset
   185             newunion = Union()
   185             newunion = Union()
   186             newunion.append(newselect)
   186             newunion.append(newselect)
   187             rql = rqlst.as_string(kwargs=self.rset.args)
   187             rql = rqlst.as_string(kwargs=self.rset.args)
   188             rqlst.parent = None
   188             rqlst.parent = None
   189         return rql
   189         return rql
   190         
   190 
   191     def view(self, __vid, rset=None, __fallback_vid=None, **kwargs):
   191     def view(self, __vid, rset=None, __fallback_vid=None, **kwargs):
   192         """shortcut to self.vreg.render method avoiding to pass self.req"""
   192         """shortcut to self.vreg.render method avoiding to pass self.req"""
   193         try:
   193         try:
   194             view = self.vreg.select_view(__vid, self.req, rset, **kwargs)
   194             view = self.vreg.select_view(__vid, self.req, rset, **kwargs)
   195         except NoSelectableObject:
   195         except NoSelectableObject:
   196             if __fallback_vid is None:
   196             if __fallback_vid is None:
   197                 raise
   197                 raise
   198             view = self.vreg.select_view(__fallback_vid, self.req, rset, **kwargs)
   198             view = self.vreg.select_view(__fallback_vid, self.req, rset, **kwargs)
   199         return view.dispatch(**kwargs)
   199         return view.dispatch(**kwargs)
   200     
   200 
   201     # url generation methods ##################################################
   201     # url generation methods ##################################################
   202     
   202 
   203     controller = 'view'
   203     controller = 'view'
   204     
   204 
   205     def build_url(self, method=None, **kwargs):
   205     def build_url(self, method=None, **kwargs):
   206         """return an absolute URL using params dictionary key/values as URL
   206         """return an absolute URL using params dictionary key/values as URL
   207         parameters. Values are automatically URL quoted, and the
   207         parameters. Values are automatically URL quoted, and the
   208         publishing method to use may be specified or will be guessed.
   208         publishing method to use may be specified or will be guessed.
   209         """
   209         """
   215                    not '_restpath' in kwargs:
   215                    not '_restpath' in kwargs:
   216                 method = self.req.relative_path(includeparams=False) or 'view'
   216                 method = self.req.relative_path(includeparams=False) or 'view'
   217         return self.req.build_url(method, **kwargs)
   217         return self.req.build_url(method, **kwargs)
   218 
   218 
   219     # various resources accessors #############################################
   219     # various resources accessors #############################################
   220     
   220 
   221     def entity(self, row, col=0):
   221     def entity(self, row, col=0):
   222         """short cut to get an entity instance for a particular row/column
   222         """short cut to get an entity instance for a particular row/column
   223         (col default to 0)
   223         (col default to 0)
   224         """
   224         """
   225         return self.rset.get_entity(row, col)
   225         return self.rset.get_entity(row, col)
   226     
   226 
   227     def complete_entity(self, row, col=0, skip_bytes=True):
   227     def complete_entity(self, row, col=0, skip_bytes=True):
   228         """short cut to get an completed entity instance for a particular
   228         """short cut to get an completed entity instance for a particular
   229         row (all instance's attributes have been fetched)
   229         row (all instance's attributes have been fetched)
   230         """
   230         """
   231         entity = self.entity(row, col)
   231         entity = self.entity(row, col)
   237         to call it ready to be inserted in html
   237         to call it ready to be inserted in html
   238         """
   238         """
   239         def rqlexec(req, rql, args=None, key=None):
   239         def rqlexec(req, rql, args=None, key=None):
   240             req.execute(rql, args, key)
   240             req.execute(rql, args, key)
   241         return self.user_callback(rqlexec, args, msg)
   241         return self.user_callback(rqlexec, args, msg)
   242         
   242 
   243     def user_callback(self, cb, args, msg=None, nonify=False):
   243     def user_callback(self, cb, args, msg=None, nonify=False):
   244         """register the given user callback and return an url to call it ready to be
   244         """register the given user callback and return an url to call it ready to be
   245         inserted in html
   245         inserted in html
   246         """
   246         """
   247         self.req.add_js('cubicweb.ajax.js')
   247         self.req.add_js('cubicweb.ajax.js')
   248         cbname = self.req.register_onetime_callback(cb, *args)
   248         cbname = self.req.register_onetime_callback(cb, *args)
   249         msg = dumps(msg or '') 
   249         msg = dumps(msg or '')
   250         return "javascript:userCallbackThenReloadPage('%s', %s)" % (
   250         return "javascript:userCallbackThenReloadPage('%s', %s)" % (
   251             cbname, msg)
   251             cbname, msg)
   252 
   252 
   253     # formating methods #######################################################
   253     # formating methods #######################################################
   254 
   254 
   291         configuration
   291         configuration
   292         """
   292         """
   293         if num:
   293         if num:
   294             return self.req.property_value('ui.float-format') % num
   294             return self.req.property_value('ui.float-format') % num
   295         return u''
   295         return u''
   296     
   296 
   297     # security related methods ################################################
   297     # security related methods ################################################
   298     
   298 
   299     def ensure_ro_rql(self, rql):
   299     def ensure_ro_rql(self, rql):
   300         """raise an exception if the given rql is not a select query"""
   300         """raise an exception if the given rql is not a select query"""
   301         first = rql.split(' ', 1)[0].lower()
   301         first = rql.split(' ', 1)[0].lower()
   302         if first in ('insert', 'set', 'delete'):
   302         if first in ('insert', 'set', 'delete'):
   303             raise Unauthorized(self.req._('only select queries are authorized'))
   303             raise Unauthorized(self.req._('only select queries are authorized'))
   304 
   304 
   305         
   305 
   306 class AppObject(AppRsetObject):
   306 class AppObject(AppRsetObject):
   307     """base class for application objects which are not selected
   307     """base class for application objects which are not selected
   308     according to a result set, only by their identifier.
   308     according to a result set, only by their identifier.
   309     
   309 
   310     Those objects may not have req, rset and cursor set.
   310     Those objects may not have req, rset and cursor set.
   311     """
   311     """
   312     
   312 
   313     @classmethod
   313     @classmethod
   314     def selected(cls, *args, **kwargs):
   314     def selected(cls, *args, **kwargs):
   315         """by default web app objects are usually instantiated on
   315         """by default web app objects are usually instantiated on
   316         selection
   316         selection
   317         """
   317         """