5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
6 """ |
6 """ |
7 __docformat__ = "restructuredtext en" |
7 __docformat__ = "restructuredtext en" |
8 |
8 |
9 from logilab.common.decorators import cached, clear_cache |
9 from logilab.common.decorators import cached, clear_cache |
10 from logilab.common.interface import extend |
|
11 |
10 |
12 from rql import RQLHelper |
11 from rql import RQLHelper |
13 |
12 |
14 from cubicweb import Binary, UnknownProperty, UnknownEid |
13 from cubicweb import Binary, UnknownProperty, UnknownEid |
15 from cubicweb.vregistry import VRegistry, ObjectNotFound, NoSelectableObject |
14 from cubicweb.vregistry import VRegistry, ObjectNotFound, NoSelectableObject |
21 selectors, with a bw compat fallback to accepts_interfaces attribute |
20 selectors, with a bw compat fallback to accepts_interfaces attribute |
22 """ |
21 """ |
23 from cubicweb.selectors import implements |
22 from cubicweb.selectors import implements |
24 try: |
23 try: |
25 # XXX deprecated |
24 # XXX deprecated |
26 return sorted(obj.accepts_interfaces) |
25 return sorted(obj.accepts_interfaces) |
27 except AttributeError: |
26 except AttributeError: |
28 try: |
27 try: |
29 impl = obj.__select__.search_selector(implements) |
28 impl = obj.__select__.search_selector(implements) |
30 if impl: |
29 if impl: |
31 return sorted(impl.expected_ifaces) |
30 return sorted(impl.expected_ifaces) |
37 return () |
36 return () |
38 |
37 |
39 |
38 |
40 class CubicWebRegistry(VRegistry): |
39 class CubicWebRegistry(VRegistry): |
41 """extend the generic VRegistry with some cubicweb specific stuff""" |
40 """extend the generic VRegistry with some cubicweb specific stuff""" |
42 |
41 |
43 def __init__(self, config, debug=None, initlog=True): |
42 def __init__(self, config, debug=None, initlog=True): |
44 if initlog: |
43 if initlog: |
45 # first init log service |
44 # first init log service |
46 config.init_log(debug=debug) |
45 config.init_log(debug=debug) |
47 super(CubicWebRegistry, self).__init__(config) |
46 super(CubicWebRegistry, self).__init__(config) |
48 self.schema = None |
47 self.schema = None |
49 self.reset() |
48 self.reset() |
50 self.initialized = False |
49 self.initialized = False |
51 |
50 |
52 def items(self): |
51 def items(self): |
53 return [item for item in self._registries.items() |
52 return [item for item in self._registries.items() |
54 if not item[0] in ('propertydefs', 'propertyvalues')] |
53 if not item[0] in ('propertydefs', 'propertyvalues')] |
55 |
54 |
56 def values(self): |
55 def values(self): |
57 return [value for key, value in self._registries.items() |
56 return [value for key, value in self._registries.items() |
58 if not key in ('propertydefs', 'propertyvalues')] |
57 if not key in ('propertydefs', 'propertyvalues')] |
59 |
58 |
60 def reset(self): |
59 def reset(self): |
61 self._registries = {} |
60 self._registries = {} |
62 self._lastmodifs = {} |
61 self._lastmodifs = {} |
63 self._needs_iface = {} |
62 self._needs_iface = {} |
64 # two special registries, propertydefs which care all the property |
63 # two special registries, propertydefs which care all the property |
66 # properties |
65 # properties |
67 self._registries['propertydefs'] = {} |
66 self._registries['propertydefs'] = {} |
68 self._registries['propertyvalues'] = self.eprop_values = {} |
67 self._registries['propertyvalues'] = self.eprop_values = {} |
69 for key, propdef in self.config.eproperty_definitions(): |
68 for key, propdef in self.config.eproperty_definitions(): |
70 self.register_property(key, **propdef) |
69 self.register_property(key, **propdef) |
71 |
70 |
72 def set_schema(self, schema): |
71 def set_schema(self, schema): |
73 """set application'schema and load application objects""" |
72 """set application'schema and load application objects""" |
74 self.schema = schema |
73 self.schema = schema |
75 clear_cache(self, 'rqlhelper') |
74 clear_cache(self, 'rqlhelper') |
76 # now we can load application's web objects |
75 # now we can load application's web objects |
77 self.register_objects(self.config.vregistry_path()) |
76 self.register_objects(self.config.vregistry_path()) |
78 |
77 |
79 def update_schema(self, schema): |
78 def update_schema(self, schema): |
80 """update .schema attribute on registered objects, necessary for some |
79 """update .schema attribute on registered objects, necessary for some |
81 tests |
80 tests |
82 """ |
81 """ |
83 self.schema = schema |
82 self.schema = schema |
108 super(CubicWebRegistry, self).register(obj, **kwargs) |
107 super(CubicWebRegistry, self).register(obj, **kwargs) |
109 # XXX bw compat |
108 # XXX bw compat |
110 ifaces = use_interfaces(obj) |
109 ifaces = use_interfaces(obj) |
111 if ifaces: |
110 if ifaces: |
112 self._needs_iface[obj] = ifaces |
111 self._needs_iface[obj] = ifaces |
113 |
112 |
114 def register_objects(self, path, force_reload=None): |
113 def register_objects(self, path, force_reload=None): |
115 """overriden to remove objects requiring a missing interface""" |
114 """overriden to remove objects requiring a missing interface""" |
116 if super(CubicWebRegistry, self).register_objects(path, force_reload): |
115 if super(CubicWebRegistry, self).register_objects(path, force_reload): |
117 # clear etype cache if you don't want to run into deep weirdness |
116 # clear etype cache if you don't want to run into deep weirdness |
118 clear_cache(self, 'etype_class') |
117 clear_cache(self, 'etype_class') |
145 self.debug('available in registry %s: %s', registry, |
144 self.debug('available in registry %s: %s', registry, |
146 sorted(objects)) |
145 sorted(objects)) |
147 for appobjects in objects.itervalues(): |
146 for appobjects in objects.itervalues(): |
148 for appobject in appobjects: |
147 for appobject in appobjects: |
149 appobject.vreg_initialization_completed() |
148 appobject.vreg_initialization_completed() |
150 |
149 |
151 @cached |
150 @cached |
152 def etype_class(self, etype): |
151 def etype_class(self, etype): |
153 """return an entity class for the given entity type. |
152 """return an entity class for the given entity type. |
154 Try to find out a specific class for this kind of entity or |
153 Try to find out a specific class for this kind of entity or |
155 default to a dump of the class registered for 'Any' |
154 default to a dump of the class registered for 'Any' |
170 pass |
169 pass |
171 else: |
170 else: |
172 # no entity class for any of the ancestors, fallback to the default |
171 # no entity class for any of the ancestors, fallback to the default |
173 # one |
172 # one |
174 cls = self.select(self.registry_objects('etypes', 'Any'), etype) |
173 cls = self.select(self.registry_objects('etypes', 'Any'), etype) |
175 # add class itself to the list of implemented interfaces, as well as the |
|
176 # Any entity class so we can select according to class using the |
|
177 # `implements` selector |
|
178 extend(cls, cls) |
|
179 extend(cls, self.etype_class('Any')) |
|
180 return cls |
174 return cls |
181 |
175 |
182 def render(self, registry, oid, req, **context): |
176 def render(self, registry, oid, req, **context): |
183 """select an object in a given registry and render it |
177 """select an object in a given registry and render it |
184 |
178 |
185 - registry: the registry's name |
179 - registry: the registry's name |
186 - oid : the view to call |
180 - oid : the view to call |
187 - req : the HTTP request |
181 - req : the HTTP request |
188 """ |
182 """ |
189 objclss = self.registry_objects(registry, oid) |
183 objclss = self.registry_objects(registry, oid) |
190 try: |
184 try: |
191 rset = context.pop('rset') |
185 rset = context.pop('rset') |
192 except KeyError: |
186 except KeyError: |
193 rset = None |
187 rset = None |
194 selected = self.select(objclss, req, rset, **context) |
188 selected = self.select(objclss, req, rset, **context) |
195 return selected.dispatch(**context) |
189 return selected.dispatch(**context) |
196 |
190 |
197 def main_template(self, req, oid='main-template', **context): |
191 def main_template(self, req, oid='main-template', **context): |
198 """display query by calling the given template (default to main), |
192 """display query by calling the given template (default to main), |
199 and returning the output as a string instead of requiring the [w]rite |
193 and returning the output as a string instead of requiring the [w]rite |
200 method as argument |
194 method as argument |
201 """ |
195 """ |
211 visualizable objects) |
205 visualizable objects) |
212 """ |
206 """ |
213 return [x for x in sorted(self.possible_objects(registry, *args, **kwargs), |
207 return [x for x in sorted(self.possible_objects(registry, *args, **kwargs), |
214 key=lambda x: x.propval('order')) |
208 key=lambda x: x.propval('order')) |
215 if x.propval('visible')] |
209 if x.propval('visible')] |
216 |
210 |
217 def possible_actions(self, req, rset, **kwargs): |
211 def possible_actions(self, req, rset, **kwargs): |
218 if rset is None: |
212 if rset is None: |
219 actions = self.possible_vobjects('actions', req, rset) |
213 actions = self.possible_vobjects('actions', req, rset) |
220 else: |
214 else: |
221 actions = rset.possible_actions() # cached implementation |
215 actions = rset.possible_actions() # cached implementation |
222 result = {} |
216 result = {} |
223 for action in actions: |
217 for action in actions: |
224 result.setdefault(action.category, []).append(action) |
218 result.setdefault(action.category, []).append(action) |
225 return result |
219 return result |
226 |
220 |
227 def possible_views(self, req, rset, **kwargs): |
221 def possible_views(self, req, rset, **kwargs): |
228 """return an iterator on possible views for this result set |
222 """return an iterator on possible views for this result set |
229 |
223 |
230 views returned are classes, not instances |
224 views returned are classes, not instances |
231 """ |
225 """ |
239 except NoSelectableObject: |
233 except NoSelectableObject: |
240 continue |
234 continue |
241 except Exception: |
235 except Exception: |
242 self.exception('error while trying to list possible %s views for %s', |
236 self.exception('error while trying to list possible %s views for %s', |
243 vid, rset) |
237 vid, rset) |
244 |
238 |
245 def select_box(self, oid, *args, **kwargs): |
239 def select_box(self, oid, *args, **kwargs): |
246 """return the most specific view according to the result set""" |
240 """return the most specific view according to the result set""" |
247 try: |
241 try: |
248 return self.select_object('boxes', oid, *args, **kwargs) |
242 return self.select_object('boxes', oid, *args, **kwargs) |
249 except NoSelectableObject: |
243 except NoSelectableObject: |
253 """return the most specific view according to the result set""" |
247 """return the most specific view according to the result set""" |
254 try: |
248 try: |
255 return self.select_object('actions', oid, *args, **kwargs) |
249 return self.select_object('actions', oid, *args, **kwargs) |
256 except NoSelectableObject: |
250 except NoSelectableObject: |
257 return |
251 return |
258 |
252 |
259 def select_component(self, cid, *args, **kwargs): |
253 def select_component(self, cid, *args, **kwargs): |
260 """return the most specific component according to the result set""" |
254 """return the most specific component according to the result set""" |
261 try: |
255 try: |
262 return self.select_object('components', cid, *args, **kwargs) |
256 return self.select_object('components', cid, *args, **kwargs) |
263 except (NoSelectableObject, ObjectNotFound): |
257 except (NoSelectableObject, ObjectNotFound): |
266 def select_view(self, __vid, req, rset, **kwargs): |
260 def select_view(self, __vid, req, rset, **kwargs): |
267 """return the most specific view according to the result set""" |
261 """return the most specific view according to the result set""" |
268 views = self.registry_objects('views', __vid) |
262 views = self.registry_objects('views', __vid) |
269 return self.select(views, req, rset, **kwargs) |
263 return self.select(views, req, rset, **kwargs) |
270 |
264 |
271 |
265 |
272 # properties handling ##################################################### |
266 # properties handling ##################################################### |
273 |
267 |
274 def user_property_keys(self, withsitewide=False): |
268 def user_property_keys(self, withsitewide=False): |
275 if withsitewide: |
269 if withsitewide: |
276 return sorted(self['propertydefs']) |
270 return sorted(self['propertydefs']) |
280 def register_property(self, key, type, help, default=None, vocabulary=None, |
274 def register_property(self, key, type, help, default=None, vocabulary=None, |
281 sitewide=False): |
275 sitewide=False): |
282 """register a given property""" |
276 """register a given property""" |
283 properties = self._registries['propertydefs'] |
277 properties = self._registries['propertydefs'] |
284 assert type in YAMS_TO_PY |
278 assert type in YAMS_TO_PY |
285 properties[key] = {'type': type, 'vocabulary': vocabulary, |
279 properties[key] = {'type': type, 'vocabulary': vocabulary, |
286 'default': default, 'help': help, |
280 'default': default, 'help': help, |
287 'sitewide': sitewide} |
281 'sitewide': sitewide} |
288 |
282 |
289 def property_info(self, key): |
283 def property_info(self, key): |
290 """return dictionary containing description associated to the given |
284 """return dictionary containing description associated to the given |
298 soft = key.split('.')[-1] |
292 soft = key.split('.')[-1] |
299 return {'type': 'String', 'sitewide': True, |
293 return {'type': 'String', 'sitewide': True, |
300 'default': None, 'vocabulary': None, |
294 'default': None, 'vocabulary': None, |
301 'help': _('%s software version of the database') % soft} |
295 'help': _('%s software version of the database') % soft} |
302 raise UnknownProperty('unregistered property %r' % key) |
296 raise UnknownProperty('unregistered property %r' % key) |
303 |
297 |
304 def property_value(self, key): |
298 def property_value(self, key): |
305 try: |
299 try: |
306 return self._registries['propertyvalues'][key] |
300 return self._registries['propertyvalues'][key] |
307 except KeyError: |
301 except KeyError: |
308 return self._registries['propertydefs'][key]['default'] |
302 return self._registries['propertydefs'][key]['default'] |
321 if callable(vocab): |
315 if callable(vocab): |
322 vocab = vocab(key, None) # XXX need a req object |
316 vocab = vocab(key, None) # XXX need a req object |
323 if not value in vocab: |
317 if not value in vocab: |
324 raise ValueError(_('unauthorized value')) |
318 raise ValueError(_('unauthorized value')) |
325 return value |
319 return value |
326 |
320 |
327 def init_properties(self, propvalues): |
321 def init_properties(self, propvalues): |
328 """init the property values registry using the given set of couple (key, value) |
322 """init the property values registry using the given set of couple (key, value) |
329 """ |
323 """ |
330 self.initialized = True |
324 self.initialized = True |
331 values = self._registries['propertyvalues'] |
325 values = self._registries['propertyvalues'] |
381 for vobject in vobjects: |
375 for vobject in vobjects: |
382 vobject.vreg = self |
376 vobject.vreg = self |
383 vobject.schema = self.schema |
377 vobject.schema = self.schema |
384 vobject.config = self.config |
378 vobject.config = self.config |
385 return super(MulCnxCubicWebRegistry, self).select(vobjects, *args, **kwargs) |
379 return super(MulCnxCubicWebRegistry, self).select(vobjects, *args, **kwargs) |
386 |
380 |
387 from datetime import datetime, date, time, timedelta |
381 from datetime import datetime, date, time, timedelta |
388 |
382 |
389 YAMS_TO_PY = { |
383 YAMS_TO_PY = { |
390 'Boolean': bool, |
384 'Boolean': bool, |
391 'String' : unicode, |
385 'String' : unicode, |