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. |
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: |
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 = { |