cwvreg.py
changeset 2650 18aec79ec3a3
parent 2613 5e19c2bb370e
child 2655 48cd71bdb5cd
equal deleted inserted replaced
2649:5d4a943695d1 2650:18aec79ec3a3
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     7 """
     7 """
     8 __docformat__ = "restructuredtext en"
     8 __docformat__ = "restructuredtext en"
     9 _ = unicode
     9 _ = unicode
    10 
    10 
    11 from logilab.common.decorators import cached, clear_cache
    11 from logilab.common.decorators import cached, clear_cache, monkeypatch
    12 from logilab.common.deprecation import  deprecated
    12 from logilab.common.deprecation import  deprecated
    13 
    13 
    14 from rql import RQLHelper
    14 from rql import RQLHelper
    15 
    15 
    16 from cubicweb import ETYPE_NAME_MAP, Binary, UnknownProperty, UnknownEid
    16 from cubicweb import (ETYPE_NAME_MAP, Binary, UnknownProperty, UnknownEid,
    17 from cubicweb.vregistry import VRegistry, ObjectNotFound, NoSelectableObject
    17                       ObjectNotFound, NoSelectableObject, RegistryNotFound)
       
    18 from cubicweb.vregistry import VRegistry, Registry
    18 from cubicweb.rtags import RTAGS
    19 from cubicweb.rtags import RTAGS
    19 
    20 
    20 
    21 
    21 def use_interfaces(obj):
    22 def use_interfaces(obj):
    22     """return interfaces used by the given object by searchinf for implements
    23     """return interfaces used by the given object by searchinf for implements
    37             print 'bad selector %s on %s' % (obj.__select__, obj)
    38             print 'bad selector %s on %s' % (obj.__select__, obj)
    38             raise
    39             raise
    39         return ()
    40         return ()
    40 
    41 
    41 
    42 
    42 class CubicWebRegistry(VRegistry):
    43 class CWRegistry(Registry):
       
    44     def __init__(self, vreg):
       
    45         super(CWRegistry, self).__init__(vreg.config)
       
    46         self.vreg = vreg
       
    47         self.schema = vreg.schema
       
    48 
       
    49     def initialization_completed(self):
       
    50         # call vreg_initialization_completed on appobjects and print
       
    51         # registry content
       
    52         for appobjects in self.itervalues():
       
    53             for appobject in appobjects:
       
    54                 appobject.vreg_initialization_completed()
       
    55 
       
    56     def render(self, __oid, req, __fallback_oid=None, rset=None, **kwargs):
       
    57         """select object, or fallback object if specified and the first one
       
    58         isn't selectable, then render it
       
    59         """
       
    60         try:
       
    61             obj = self.select(__oid, req, rset=rset, **kwargs)
       
    62         except NoSelectableObject:
       
    63             if __fallback_oid is None:
       
    64                 raise
       
    65             obj = self.select(__fallback_oid, req, **kwargs)
       
    66         return obj.render(**kwargs)
       
    67 
       
    68     def select_vobject(self, oid, *args, **kwargs):
       
    69         selected = self.select_object(oid, *args, **kwargs)
       
    70         if selected and selected.propval('visible'):
       
    71             return selected
       
    72         return None
       
    73 
       
    74     def possible_vobjects(self, *args, **kwargs):
       
    75         """return an ordered list of possible app objects in a given registry,
       
    76         supposing they support the 'visible' and 'order' properties (as most
       
    77         visualizable objects)
       
    78         """
       
    79         return sorted([x for x in self.possible_objects(*args, **kwargs)
       
    80                        if x.propval('visible')],
       
    81                       key=lambda x: x.propval('order'))
       
    82 
       
    83 
       
    84 VRegistry.REGISTRY_FACTORY[None] = CWRegistry
       
    85 
       
    86 
       
    87 class ETypeRegistry(CWRegistry):
       
    88 
       
    89     def initialization_completed(self):
       
    90         super(ETypeRegistry, self).initialization_completed()
       
    91         # clear etype cache if you don't want to run into deep weirdness
       
    92         clear_cache(self, 'etype_class')
       
    93 
       
    94     def register(self, obj, **kwargs):
       
    95         oid = kwargs.get('oid') or obj.id
       
    96         if oid != 'Any' and not oid in self.schema:
       
    97             self.error('don\'t register %s, %s type not defined in the '
       
    98                        'schema', obj, obj.id)
       
    99             return
       
   100         kwargs['clear'] = True
       
   101         super(ETypeRegistry, self).register(obj, **kwargs)
       
   102 
       
   103     @cached
       
   104     def etype_class(self, etype):
       
   105         """return an entity class for the given entity type.
       
   106         Try to find out a specific class for this kind of entity or
       
   107         default to a dump of the class registered for 'Any'
       
   108         """
       
   109         etype = str(etype)
       
   110         if etype == 'Any':
       
   111             return self.select('Any', 'Any')
       
   112         eschema = self.schema.eschema(etype)
       
   113         baseschemas = [eschema] + eschema.ancestors()
       
   114         # browse ancestors from most specific to most generic and
       
   115         # try to find an associated custom entity class
       
   116         for baseschema in baseschemas:
       
   117             try:
       
   118                 btype = ETYPE_NAME_MAP[baseschema]
       
   119             except KeyError:
       
   120                 btype = str(baseschema)
       
   121             try:
       
   122                 cls = self.select(btype, etype)
       
   123                 break
       
   124             except ObjectNotFound:
       
   125                 pass
       
   126         else:
       
   127             # no entity class for any of the ancestors, fallback to the default
       
   128             # one
       
   129             cls = self.select('Any', etype)
       
   130         return cls
       
   131 
       
   132 VRegistry.REGISTRY_FACTORY['etypes'] = ETypeRegistry
       
   133 
       
   134 
       
   135 class ViewsRegistry(CWRegistry):
       
   136 
       
   137     def main_template(self, req, oid='main-template', **kwargs):
       
   138         """display query by calling the given template (default to main),
       
   139         and returning the output as a string instead of requiring the [w]rite
       
   140         method as argument
       
   141         """
       
   142         res = self.render(oid, req, **kwargs)
       
   143         if isinstance(res, unicode):
       
   144             return res.encode(req.encoding)
       
   145         assert isinstance(res, str)
       
   146         return res
       
   147 
       
   148     def possible_views(self, req, rset=None, **kwargs):
       
   149         """return an iterator on possible views for this result set
       
   150 
       
   151         views returned are classes, not instances
       
   152         """
       
   153         for vid, views in self.items():
       
   154             if vid[0] == '_':
       
   155                 continue
       
   156             try:
       
   157                 view = self.select_best(views, req, rset=rset, **kwargs)
       
   158                 if view.linkable():
       
   159                     yield view
       
   160             except NoSelectableObject:
       
   161                 continue
       
   162             except Exception:
       
   163                 self.exception('error while trying to select %s view for %s',
       
   164                                vid, rset)
       
   165 
       
   166 VRegistry.REGISTRY_FACTORY['views'] = ViewsRegistry
       
   167 
       
   168 
       
   169 class ActionsRegistry(CWRegistry):
       
   170 
       
   171     def possible_actions(self, req, rset=None, **kwargs):
       
   172         if rset is None:
       
   173             actions = self.possible_vobjects(req, rset=rset, **kwargs)
       
   174         else:
       
   175             actions = rset.possible_actions(**kwargs) # cached implementation
       
   176         result = {}
       
   177         for action in actions:
       
   178             result.setdefault(action.category, []).append(action)
       
   179         return result
       
   180 
       
   181 VRegistry.REGISTRY_FACTORY['actions'] = ActionsRegistry
       
   182 
       
   183 
       
   184 
       
   185 class CubicWebVRegistry(VRegistry):
    43     """Central registry for the cubicweb instance, extending the generic
   186     """Central registry for the cubicweb instance, extending the generic
    44     VRegistry with some cubicweb specific stuff.
   187     VRegistry with some cubicweb specific stuff.
    45 
   188 
    46     This is one of the central object in cubicweb instance, coupling
   189     This is one of the central object in cubicweb instance, coupling
    47     dynamically loaded objects with the schema and the configuration objects.
   190     dynamically loaded objects with the schema and the configuration objects.
    63 
   206 
    64     def __init__(self, config, debug=None, initlog=True):
   207     def __init__(self, config, debug=None, initlog=True):
    65         if initlog:
   208         if initlog:
    66             # first init log service
   209             # first init log service
    67             config.init_log(debug=debug)
   210             config.init_log(debug=debug)
    68         super(CubicWebRegistry, self).__init__(config)
   211         super(CubicWebVRegistry, self).__init__(config)
    69         self.schema = None
   212         self.schema = None
    70         self.reset()
   213         self.reset()
    71         self.initialized = False
   214         self.initialized = False
    72 
   215 
       
   216     def setdefault(self, regid):
       
   217         try:
       
   218             return self[regid]
       
   219         except RegistryNotFound:
       
   220             self[regid] = self.registry_class(regid)(self)
       
   221             return self[regid]
       
   222 
    73     def items(self):
   223     def items(self):
    74         return [item for item in self._registries.items()
   224         return [item for item in super(CubicWebVRegistry, self).items()
    75                 if not item[0] in ('propertydefs', 'propertyvalues')]
   225                 if not item[0] in ('propertydefs', 'propertyvalues')]
    76 
   226 
    77     def values(self):
   227     def values(self):
    78         return [value for key, value in self._registries.items()
   228         return [value for key, value in self.items()]
    79                 if not key in ('propertydefs', 'propertyvalues')]
       
    80 
   229 
    81     def reset(self):
   230     def reset(self):
    82         self._registries = {}
   231         super(CubicWebVRegistry, self).reset()
    83         self._lastmodifs = {}
       
    84         self._needs_iface = {}
   232         self._needs_iface = {}
    85         # two special registries, propertydefs which care all the property
   233         # two special registries, propertydefs which care all the property
    86         # definitions, and propertyvals which contains values for those
   234         # definitions, and propertyvals which contains values for those
    87         # properties
   235         # properties
    88         self._registries['propertydefs'] = {}
   236         self['propertydefs'] = {}
    89         self._registries['propertyvalues'] = self.eprop_values = {}
   237         self['propertyvalues'] = self.eprop_values = {}
    90         for key, propdef in self.config.eproperty_definitions():
   238         for key, propdef in self.config.eproperty_definitions():
    91             self.register_property(key, **propdef)
   239             self.register_property(key, **propdef)
    92 
   240 
    93     def set_schema(self, schema):
   241     def set_schema(self, schema):
    94         """set instance'schema and load application objects"""
   242         """set instance'schema and load application objects"""
   105     def update_schema(self, schema):
   253     def update_schema(self, schema):
   106         """update .schema attribute on registered objects, necessary for some
   254         """update .schema attribute on registered objects, necessary for some
   107         tests
   255         tests
   108         """
   256         """
   109         self.schema = schema
   257         self.schema = schema
   110         for registry, regcontent in self._registries.items():
   258         for registry, regcontent in self.items():
   111             if registry in ('propertydefs', 'propertyvalues'):
       
   112                 continue
       
   113             for objects in regcontent.values():
   259             for objects in regcontent.values():
   114                 for obj in objects:
   260                 for obj in objects:
   115                     obj.schema = schema
   261                     obj.schema = schema
   116 
   262 
   117     def register_if_interface_found(self, obj, ifaces, **kwargs):
   263     def register_if_interface_found(self, obj, ifaces, **kwargs):
   123             self._needs_iface[obj] = (ifaces,)
   269             self._needs_iface[obj] = (ifaces,)
   124         else:
   270         else:
   125             self._needs_iface[obj] = ifaces
   271             self._needs_iface[obj] = ifaces
   126 
   272 
   127     def register(self, obj, **kwargs):
   273     def register(self, obj, **kwargs):
   128         if kwargs.get('registryname', obj.__registry__) == 'etypes':
   274         super(CubicWebVRegistry, self).register(obj, **kwargs)
   129             if obj.id != 'Any' and not obj.id in self.schema:
       
   130                 self.error('don\'t register %s, %s type not defined in the '
       
   131                            'schema', obj, obj.id)
       
   132                 return
       
   133             kwargs['clear'] = True
       
   134         super(CubicWebRegistry, self).register(obj, **kwargs)
       
   135         # XXX bw compat
   275         # XXX bw compat
   136         ifaces = use_interfaces(obj)
   276         ifaces = use_interfaces(obj)
   137         if ifaces:
   277         if ifaces:
   138             self._needs_iface[obj] = ifaces
   278             self._needs_iface[obj] = ifaces
   139 
   279 
   141         """overriden to remove objects requiring a missing interface"""
   281         """overriden to remove objects requiring a missing interface"""
   142         extrapath = {}
   282         extrapath = {}
   143         for cubesdir in self.config.cubes_search_path():
   283         for cubesdir in self.config.cubes_search_path():
   144             if cubesdir != self.config.CUBES_DIR:
   284             if cubesdir != self.config.CUBES_DIR:
   145                 extrapath[cubesdir] = 'cubes'
   285                 extrapath[cubesdir] = 'cubes'
   146         if super(CubicWebRegistry, self).register_objects(path, force_reload,
   286         if super(CubicWebVRegistry, self).register_objects(path, force_reload,
   147                                                           extrapath):
   287                                                           extrapath):
   148             self.initialization_completed()
   288             self.initialization_completed()
   149             # call vreg_initialization_completed on appobjects and print
       
   150             # registry content
       
   151             for registry, objects in self.items():
       
   152                 self.debug('available in registry %s: %s', registry,
       
   153                            sorted(objects))
       
   154                 for appobjects in objects.itervalues():
       
   155                     for appobject in appobjects:
       
   156                         appobject.vreg_initialization_completed()
       
   157             # don't check rtags if we don't want to cleanup_interface_sobjects
   289             # don't check rtags if we don't want to cleanup_interface_sobjects
   158             for rtag in RTAGS:
   290             for rtag in RTAGS:
   159                 rtag.init(self.schema,
   291                 rtag.init(self.schema,
   160                           check=self.config.cleanup_interface_sobjects)
   292                           check=self.config.cleanup_interface_sobjects)
   161 
   293 
   162     def initialization_completed(self):
   294     def initialization_completed(self):
   163         # clear etype cache if you don't want to run into deep weirdness
   295         for regname, reg in self.items():
   164         clear_cache(self, 'etype_class')
   296             self.debug('available in registry %s: %s', regname, sorted(reg))
       
   297             reg.initialization_completed()
   165         # we may want to keep interface dependent objects (e.g.for i18n
   298         # we may want to keep interface dependent objects (e.g.for i18n
   166         # catalog generation)
   299         # catalog generation)
   167         if self.config.cleanup_interface_sobjects:
   300         if self.config.cleanup_interface_sobjects:
   168             # remove vobjects that don't support any available interface
   301             # remove vobjects that don't support any available interface
   169             implemented_interfaces = set()
   302             implemented_interfaces = set()
   170             if 'Any' in self.get('etypes', ()):
   303             if 'Any' in self.get('etypes', ()):
   171                 for etype in self.schema.entities():
   304                 for etype in self.schema.entities():
   172                     cls = self.etype_class(etype)
   305                     cls = self['etypes'].etype_class(etype)
   173                     for iface in cls.__implements__:
   306                     for iface in cls.__implements__:
   174                         implemented_interfaces.update(iface.__mro__)
   307                         implemented_interfaces.update(iface.__mro__)
   175                     implemented_interfaces.update(cls.__mro__)
   308                     implemented_interfaces.update(cls.__mro__)
   176             for obj, ifaces in self._needs_iface.items():
   309             for obj, ifaces in self._needs_iface.items():
   177                 ifaces = frozenset(isinstance(iface, basestring)
   310                 ifaces = frozenset(isinstance(iface, basestring)
   178                                    and iface in self.schema
   311                                    and iface in self.schema
   179                                    and self.etype_class(iface)
   312                                    and self['etypes'].etype_class(iface)
   180                                    or iface
   313                                    or iface
   181                                    for iface in ifaces)
   314                                    for iface in ifaces)
   182                 if not ('Any' in ifaces or ifaces & implemented_interfaces):
   315                 if not ('Any' in ifaces or ifaces & implemented_interfaces):
   183                     self.debug('kicking vobject %s (no implemented '
   316                     self.debug('kicking vobject %s (no implemented '
   184                                'interface among %s)', obj, ifaces)
   317                                'interface among %s)', obj, ifaces)
   185                     self.unregister(obj)
   318                     self.unregister(obj)
   186         # clear needs_iface so we don't try to remove some not-anymore-in
   319         # clear needs_iface so we don't try to remove some not-anymore-in
   187         # objects on automatic reloading
   320         # objects on automatic reloading
   188         self._needs_iface.clear()
   321         self._needs_iface.clear()
   189 
   322 
       
   323     def parse(self, session, rql, args=None):
       
   324         rqlst = self.rqlhelper.parse(rql)
       
   325         def type_from_eid(eid, session=session):
       
   326             return session.describe(eid)[0]
       
   327         try:
       
   328             self.rqlhelper.compute_solutions(rqlst, {'eid': type_from_eid}, args)
       
   329         except UnknownEid:
       
   330             for select in rqlst.children:
       
   331                 select.solutions = []
       
   332         return rqlst
       
   333 
       
   334     @property
   190     @cached
   335     @cached
       
   336     def rqlhelper(self):
       
   337         return RQLHelper(self.schema,
       
   338                          special_relations={'eid': 'uid', 'has_text': 'fti'})
       
   339 
       
   340 
       
   341     @deprecated('use vreg["etypes"].etype_class(etype)')
   191     def etype_class(self, etype):
   342     def etype_class(self, etype):
   192         """return an entity class for the given entity type.
   343         return self["etypes"].etype_class(etype)
   193         Try to find out a specific class for this kind of entity or
   344 
   194         default to a dump of the class registered for 'Any'
   345     @deprecated('use vreg["views"].main_template(*args, **kwargs)')
   195         """
       
   196         etype = str(etype)
       
   197         if etype == 'Any':
       
   198             return self.select('etypes', 'Any', 'Any')
       
   199         eschema = self.schema.eschema(etype)
       
   200         baseschemas = [eschema] + eschema.ancestors()
       
   201         # browse ancestors from most specific to most generic and
       
   202         # try to find an associated custom entity class
       
   203         for baseschema in baseschemas:
       
   204             try:
       
   205                 btype = ETYPE_NAME_MAP[baseschema]
       
   206             except KeyError:
       
   207                 btype = str(baseschema)
       
   208             try:
       
   209                 cls = self.select('etypes', btype, etype)
       
   210                 break
       
   211             except ObjectNotFound:
       
   212                 pass
       
   213         else:
       
   214             # no entity class for any of the ancestors, fallback to the default
       
   215             # one
       
   216             cls = self.select('etypes', 'Any', etype)
       
   217         return cls
       
   218 
       
   219     def render(self, __oid, req, __fallback_oid=None, __registry='views',
       
   220                rset=None, **kwargs):
       
   221         """select object, or fallback object if specified and the first one
       
   222         isn't selectable, then render it
       
   223         """
       
   224         try:
       
   225             obj = self.select(__registry, __oid, req, rset=rset, **kwargs)
       
   226         except NoSelectableObject:
       
   227             if __fallback_oid is None:
       
   228                 raise
       
   229             obj = self.select(__registry, __fallback_oid, req, rset=rset,
       
   230                               **kwargs)
       
   231         return obj.render(**kwargs)
       
   232 
       
   233     def main_template(self, req, oid='main-template', **context):
   346     def main_template(self, req, oid='main-template', **context):
   234         """display query by calling the given template (default to main),
   347         return self["views"].main_template(req, oid, **context)
   235         and returning the output as a string instead of requiring the [w]rite
   348 
   236         method as argument
   349     @deprecated('use vreg[registry].possible_vobjects(*args, **kwargs)')
   237         """
       
   238         res = self.render(oid, req, **context)
       
   239         if isinstance(res, unicode):
       
   240             return res.encode(req.encoding)
       
   241         assert isinstance(res, str)
       
   242         return res
       
   243 
       
   244     def select_vobject(self, registry, oid, *args, **kwargs):
       
   245         selected = self.select_object(registry, oid, *args, **kwargs)
       
   246         if selected and selected.propval('visible'):
       
   247             return selected
       
   248         return None
       
   249 
       
   250     def possible_vobjects(self, registry, *args, **kwargs):
   350     def possible_vobjects(self, registry, *args, **kwargs):
   251         """return an ordered list of possible app objects in a given registry,
   351         return self[registry].possible_vobjects(*args, **kwargs)
   252         supposing they support the 'visible' and 'order' properties (as most
   352 
   253         visualizable objects)
   353     @deprecated('use vreg["actions"].possible_actions(*args, **kwargs)')
   254         """
       
   255         return [x for x in sorted(self.possible_objects(registry, *args, **kwargs),
       
   256                                   key=lambda x: x.propval('order'))
       
   257                 if x.propval('visible')]
       
   258 
       
   259     def possible_actions(self, req, rset=None, **kwargs):
   354     def possible_actions(self, req, rset=None, **kwargs):
   260         if rset is None:
   355         return self["actions"].possible_actions(req, rest=rset, **kwargs)
   261             actions = self.possible_vobjects('actions', req, rset=rset, **kwargs)
       
   262         else:
       
   263             actions = rset.possible_actions(**kwargs) # cached implementation
       
   264         result = {}
       
   265         for action in actions:
       
   266             result.setdefault(action.category, []).append(action)
       
   267         return result
       
   268 
       
   269     def possible_views(self, req, rset=None, **kwargs):
       
   270         """return an iterator on possible views for this result set
       
   271 
       
   272         views returned are classes, not instances
       
   273         """
       
   274         for vid, views in self.registry('views').items():
       
   275             if vid[0] == '_':
       
   276                 continue
       
   277             try:
       
   278                 view = self.select_best(views, req, rset=rset, **kwargs)
       
   279                 if view.linkable():
       
   280                     yield view
       
   281             except NoSelectableObject:
       
   282                 continue
       
   283             except Exception:
       
   284                 self.exception('error while trying to select %s view for %s',
       
   285                                vid, rset)
       
   286 
   356 
   287     @deprecated("use .select_object('boxes', ...)")
   357     @deprecated("use .select_object('boxes', ...)")
   288     def select_box(self, oid, *args, **kwargs):
   358     def select_box(self, oid, *args, **kwargs):
   289         """return the most specific view according to the result set"""
   359         return self['boxes'].select_object(oid, *args, **kwargs)
   290         return self.select_object('boxes', oid, *args, **kwargs)
       
   291 
   360 
   292     @deprecated("use .select_object('components', ...)")
   361     @deprecated("use .select_object('components', ...)")
   293     def select_component(self, cid, *args, **kwargs):
   362     def select_component(self, cid, *args, **kwargs):
   294         """return the most specific component according to the result set"""
   363         return self['components'].select_object(cid, *args, **kwargs)
   295         return self.select_object('components', cid, *args, **kwargs)
       
   296 
   364 
   297     @deprecated("use .select_object('actions', ...)")
   365     @deprecated("use .select_object('actions', ...)")
   298     def select_action(self, oid, *args, **kwargs):
   366     def select_action(self, oid, *args, **kwargs):
   299         """return the most specific view according to the result set"""
   367         return self['actions'].select_object(oid, *args, **kwargs)
   300         return self.select_object('actions', oid, *args, **kwargs)
       
   301 
   368 
   302     @deprecated("use .select('views', ...)")
   369     @deprecated("use .select('views', ...)")
   303     def select_view(self, __vid, req, rset=None, **kwargs):
   370     def select_view(self, __vid, req, rset=None, **kwargs):
   304         """return the most specific view according to the result set"""
   371         return self['views'].select(__vid, req, rset=rset, **kwargs)
   305         return self.select('views', __vid, req, rset=rset, **kwargs)
       
   306 
   372 
   307     # properties handling #####################################################
   373     # properties handling #####################################################
   308 
   374 
   309     def user_property_keys(self, withsitewide=False):
   375     def user_property_keys(self, withsitewide=False):
   310         if withsitewide:
   376         if withsitewide:
   314                       if not kd['sitewide'] and not k.startswith('sources.'))
   380                       if not kd['sitewide'] and not k.startswith('sources.'))
   315 
   381 
   316     def register_property(self, key, type, help, default=None, vocabulary=None,
   382     def register_property(self, key, type, help, default=None, vocabulary=None,
   317                           sitewide=False):
   383                           sitewide=False):
   318         """register a given property"""
   384         """register a given property"""
   319         properties = self._registries['propertydefs']
   385         properties = self['propertydefs']
   320         assert type in YAMS_TO_PY
   386         assert type in YAMS_TO_PY
   321         properties[key] = {'type': type, 'vocabulary': vocabulary,
   387         properties[key] = {'type': type, 'vocabulary': vocabulary,
   322                            'default': default, 'help': help,
   388                            'default': default, 'help': help,
   323                            'sitewide': sitewide}
   389                            'sitewide': sitewide}
   324 
   390 
   326         """return dictionary containing description associated to the given
   392         """return dictionary containing description associated to the given
   327         property key (including type, defaut value, help and a site wide
   393         property key (including type, defaut value, help and a site wide
   328         boolean)
   394         boolean)
   329         """
   395         """
   330         try:
   396         try:
   331             return self._registries['propertydefs'][key]
   397             return self['propertydefs'][key]
   332         except KeyError:
   398         except KeyError:
   333             if key.startswith('system.version.'):
   399             if key.startswith('system.version.'):
   334                 soft = key.split('.')[-1]
   400                 soft = key.split('.')[-1]
   335                 return {'type': 'String', 'sitewide': True,
   401                 return {'type': 'String', 'sitewide': True,
   336                         'default': None, 'vocabulary': None,
   402                         'default': None, 'vocabulary': None,
   337                         'help': _('%s software version of the database') % soft}
   403                         'help': _('%s software version of the database') % soft}
   338             raise UnknownProperty('unregistered property %r' % key)
   404             raise UnknownProperty('unregistered property %r' % key)
   339 
   405 
   340     def property_value(self, key):
   406     def property_value(self, key):
   341         try:
   407         try:
   342             return self._registries['propertyvalues'][key]
   408             return self['propertyvalues'][key]
   343         except KeyError:
   409         except KeyError:
   344             return self._registries['propertydefs'][key]['default']
   410             return self['propertydefs'][key]['default']
   345 
   411 
   346     def typed_value(self, key, value):
   412     def typed_value(self, key, value):
   347         """value is an unicode string, return it correctly typed. Let potential
   413         """value is an unicode string, return it correctly typed. Let potential
   348         type error propagates.
   414         type error propagates.
   349         """
   415         """
   362 
   428 
   363     def init_properties(self, propvalues):
   429     def init_properties(self, propvalues):
   364         """init the property values registry using the given set of couple (key, value)
   430         """init the property values registry using the given set of couple (key, value)
   365         """
   431         """
   366         self.initialized = True
   432         self.initialized = True
   367         values = self._registries['propertyvalues']
   433         values = self['propertyvalues']
   368         for key, val in propvalues:
   434         for key, val in propvalues:
   369             try:
   435             try:
   370                 values[key] = self.typed_value(key, val)
   436                 values[key] = self.typed_value(key, val)
   371             except ValueError:
   437             except ValueError:
   372                 self.warning('%s (you should probably delete that property '
   438                 self.warning('%s (you should probably delete that property '
   373                              'from the database)', ex)
   439                              'from the database)', ex)
   374             except UnknownProperty, ex:
   440             except UnknownProperty, ex:
   375                 self.warning('%s (you should probably delete that property '
   441                 self.warning('%s (you should probably delete that property '
   376                              'from the database)', ex)
   442                              'from the database)', ex)
   377 
       
   378     def parse(self, session, rql, args=None):
       
   379         rqlst = self.rqlhelper.parse(rql)
       
   380         def type_from_eid(eid, session=session):
       
   381             return session.describe(eid)[0]
       
   382         try:
       
   383             self.rqlhelper.compute_solutions(rqlst, {'eid': type_from_eid}, args)
       
   384         except UnknownEid:
       
   385             for select in rqlst.children:
       
   386                 select.solutions = []
       
   387         return rqlst
       
   388 
       
   389     @property
       
   390     @cached
       
   391     def rqlhelper(self):
       
   392         return RQLHelper(self.schema,
       
   393                          special_relations={'eid': 'uid', 'has_text': 'fti'})
       
   394 
       
   395 
       
   396 class MulCnxCubicWebRegistry(CubicWebRegistry):
       
   397     """special registry to be used when an application has to deal with
       
   398     connections to differents repository. This class add some additional wrapper
       
   399     trying to hide buggy class attributes since classes are not designed to be
       
   400     shared among multiple registries.
       
   401     """
       
   402     def etype_class(self, etype):
       
   403         """return an entity class for the given entity type.
       
   404         Try to find out a specific class for this kind of entity or
       
   405         default to a dump of the class registered for 'Any'
       
   406         """
       
   407         usercls = super(MulCnxCubicWebRegistry, self).etype_class(etype)
       
   408         if etype == 'Any':
       
   409             return usercls
       
   410         usercls.e_schema = self.schema.eschema(etype)
       
   411         return usercls
       
   412 
       
   413     def select_best(self, vobjects, *args, **kwargs):
       
   414         """return an instance of the most specific object according
       
   415         to parameters
       
   416 
       
   417         raise NoSelectableObject if no object apply
       
   418         """
       
   419         for vobjectcls in vobjects:
       
   420             self._fix_cls_attrs(vobjectcls)
       
   421         selected = super(MulCnxCubicWebRegistry, self).select_best(
       
   422             vobjects, *args, **kwargs)
       
   423         # redo the same thing on the instance so it won't use equivalent class
       
   424         # attributes (which may change)
       
   425         self._fix_cls_attrs(selected)
       
   426         return selected
       
   427 
       
   428     def _fix_cls_attrs(self, vobject):
       
   429         vobject.vreg = self
       
   430         vobject.schema = self.schema
       
   431         vobject.config = self.config
       
   432 
   443 
   433 
   444 
   434 from datetime import datetime, date, time, timedelta
   445 from datetime import datetime, date, time, timedelta
   435 
   446 
   436 YAMS_TO_PY = {
   447 YAMS_TO_PY = {