5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
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 |
9 |
10 from itertools import cycle |
|
11 |
|
12 from logilab.mtconverter import html_escape |
10 from logilab.mtconverter import html_escape |
13 from yams import schema2dot as s2d |
11 from yams import schema2dot as s2d |
14 |
12 |
15 from cubicweb.selectors import implements, yes |
13 from cubicweb.selectors import implements, yes |
|
14 from cubicweb.schema import META_RELATIONS_TYPES, SCHEMA_TYPES |
16 from cubicweb.schemaviewer import SchemaViewer |
15 from cubicweb.schemaviewer import SchemaViewer |
17 from cubicweb.view import EntityView, StartupView |
16 from cubicweb.view import EntityView, StartupView |
18 from cubicweb.common import tags, uilib |
17 from cubicweb.common import tags, uilib |
19 from cubicweb.web import action |
18 from cubicweb.web import action |
20 from cubicweb.web.views import TmpFileViewMixin, primary, baseviews, tabs |
19 from cubicweb.web.views import TmpFileViewMixin, primary, baseviews, tabs |
21 from cubicweb.web.facet import AttributeFacet |
20 from cubicweb.web.facet import AttributeFacet |
22 |
21 |
|
22 SKIP_TYPES = set() |
|
23 SKIP_TYPES.update(META_RELATIONS_TYPES) |
|
24 SKIP_TYPES.update(SCHEMA_TYPES) |
23 |
25 |
24 class ViewSchemaAction(action.Action): |
26 class ViewSchemaAction(action.Action): |
25 id = 'schema' |
27 id = 'schema' |
26 __select__ = yes() |
28 __select__ = yes() |
27 |
29 |
54 self.w(u'<em class="finalentity">') |
56 self.w(u'<em class="finalentity">') |
55 super(CWETypeOneLineView, self).cell_call(row, col, **kwargs) |
57 super(CWETypeOneLineView, self).cell_call(row, col, **kwargs) |
56 if final: |
58 if final: |
57 self.w(u'</em>') |
59 self.w(u'</em>') |
58 |
60 |
59 SKIPPED_RELS = ('is', 'is_instance_of', 'identity', 'created_by', 'owned_by', |
|
60 'has_text',) |
|
61 |
61 |
62 class CWETypePrimaryView(tabs.TabsMixin, primary.PrimaryView): |
62 class CWETypePrimaryView(tabs.TabsMixin, primary.PrimaryView): |
63 __select__ = implements('CWEType') |
63 __select__ = implements('CWEType') |
64 title = _('in memory entity schema') |
64 title = _('in memory entity schema') |
65 main_related_section = False |
65 main_related_section = False |
66 skip_rels = SKIPPED_RELS |
|
67 tabs = [_('cwetype-schema-text'), _('cwetype-schema-image'), |
66 tabs = [_('cwetype-schema-text'), _('cwetype-schema-image'), |
68 _('cwetype-schema-permissions'), _('cwetype-workflow')] |
67 _('cwetype-schema-permissions'), _('cwetype-workflow')] |
69 default_tab = 'cwetype-schema-text' |
68 default_tab = 'cwetype-schema-text' |
70 |
69 |
71 def render_entity(self, entity): |
70 def render_entity(self, entity): |
184 alt=msg)) |
183 alt=msg)) |
185 |
184 |
186 |
185 |
187 # schema images ############################################################### |
186 # schema images ############################################################### |
188 |
187 |
189 class RestrictedSchemaDotPropsHandler(s2d.SchemaDotPropsHandler): |
188 class RestrictedSchemaVisitorMixIn(object): |
190 def __init__(self, req): |
189 def __init__(self, req, *args, **kwargs): |
191 # FIXME: colors are arbitrary |
190 super(RestrictedSchemaVisitorMixIn, self).__init__(*args, **kwargs) |
192 self.nextcolor = cycle( ('#aa0000', '#00aa00', '#0000aa', |
|
193 '#000000', '#888888') ).next |
|
194 self.req = req |
191 self.req = req |
195 |
192 |
196 def display_attr(self, rschema): |
193 def should_display_schema(self, schema): |
197 return not rschema.meta and (rschema.has_local_role('read') |
194 return (super(RestrictedSchemaVisitorMixIn, self).should_display_schema(schema) |
198 or rschema.has_perm(self.req, 'read')) |
195 and rschema.has_local_role('read') or rschema.has_perm(self.req, 'read')) |
199 |
196 |
200 # XXX remove this method once yams > 0.20 is out |
197 def should_display_attr(self, schema): |
201 def node_properties(self, eschema): |
198 return (super(RestrictedSchemaVisitorMixIn, self).should_display_attr(schema) |
202 """return default DOT drawing options for an entity schema""" |
199 and rschema.has_local_role('read') or rschema.has_perm(self.req, 'read')) |
203 label = ['{', eschema.type, '|'] |
200 |
204 label.append(r'\l'.join(rel.type for rel in eschema.subject_relations() |
201 |
205 if rel.final and self.display_attr(rel))) |
202 class FullSchemaVisitor(RestrictedSchemaVisitorMixIn, s2d.FullSchemaVisitor): |
206 label.append(r'\l}') # trailing \l ensure alignement of the last one |
|
207 return {'label' : ''.join(label), 'shape' : "record", |
|
208 'fontname' : "Courier", 'style' : "filled"} |
|
209 |
|
210 def edge_properties(self, rschema, subjnode, objnode): |
|
211 kwargs = super(RestrictedSchemaDotPropsHandler, self).edge_properties(rschema, subjnode, objnode) |
|
212 # symetric rels are handled differently, let yams decide what's best |
|
213 if not rschema.symetric: |
|
214 kwargs['color'] = self.nextcolor() |
|
215 kwargs['fontcolor'] = kwargs['color'] |
|
216 # dot label decoration is just awful (1 line underlining the label |
|
217 # + 1 line going to the closest edge spline point) |
|
218 kwargs['decorate'] = 'false' |
|
219 return kwargs |
|
220 |
|
221 |
|
222 class RestrictedSchemaVisitorMiIn: |
|
223 def __init__(self, req, *args, **kwargs): |
|
224 # hack hack hack |
|
225 assert len(self.__class__.__bases__) == 2 |
|
226 self.__parent = self.__class__.__bases__[1] |
|
227 self.__parent.__init__(self, *args, **kwargs) |
|
228 self.req = req |
|
229 |
|
230 def nodes(self): |
|
231 for etype, eschema in self.__parent.nodes(self): |
|
232 if eschema.has_local_role('read') or eschema.has_perm(self.req, 'read'): |
|
233 yield eschema.type, eschema |
|
234 |
|
235 def edges(self): |
|
236 for setype, oetype, rschema in self.__parent.edges(self): |
|
237 if rschema.has_local_role('read') or rschema.has_perm(self.req, 'read'): |
|
238 yield setype, oetype, rschema |
|
239 |
|
240 |
|
241 class FullSchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.FullSchemaVisitor): |
|
242 pass |
203 pass |
243 |
204 |
244 class OneHopESchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.OneHopESchemaVisitor): |
205 class OneHopESchemaVisitor(RestrictedSchemaVisitorMixIn, |
|
206 s2d.OneHopESchemaVisitor): |
245 pass |
207 pass |
246 |
208 |
247 class OneHopRSchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.OneHopRSchemaVisitor): |
209 class OneHopRSchemaVisitor(RestrictedSchemaVisitorMixIn, |
|
210 s2d.OneHopRSchemaVisitor): |
248 pass |
211 pass |
249 |
212 |
250 |
213 |
251 class SchemaImageView(TmpFileViewMixin, StartupView): |
214 class SchemaImageView(TmpFileViewMixin, StartupView): |
252 id = 'schemagraph' |
215 id = 'schemagraph' |
253 |
|
254 content_type = 'image/png' |
216 content_type = 'image/png' |
255 skip_rels = SKIPPED_RELS |
217 |
256 def _generate(self, tmpfile): |
218 def _generate(self, tmpfile): |
257 """display global schema information""" |
219 """display global schema information""" |
258 skipmeta = not int(self.req.form.get('withmeta', 0)) |
220 skipmeta = not int(self.req.form.get('withmeta', 0)) |
259 visitor = FullSchemaVisitor(self.req, self.schema, skiprels=self.skip_rels, skipmeta=skipmeta) |
221 visitor = FullSchemaVisitor(self.req, self.schema, |
260 s2d.schema2dot(outputfile=tmpfile, visitor=visitor, |
222 skiptypes=skipmeta and SKIP_TYPES or ()) |
261 prophdlr=RestrictedSchemaDotPropsHandler(self.req)) |
223 s2d.schema2dot(outputfile=tmpfile, visitor=visitor) |
262 |
224 |
263 class CWETypeSchemaImageView(TmpFileViewMixin, EntityView): |
225 class CWETypeSchemaImageView(TmpFileViewMixin, EntityView): |
264 id = 'schemagraph' |
226 id = 'schemagraph' |
265 __select__ = implements('CWEType') |
227 __select__ = implements('CWEType') |
266 |
|
267 content_type = 'image/png' |
228 content_type = 'image/png' |
268 skip_rels = SKIPPED_RELS |
|
269 |
229 |
270 def _generate(self, tmpfile): |
230 def _generate(self, tmpfile): |
271 """display schema information for an entity""" |
231 """display schema information for an entity""" |
272 entity = self.entity(self.row, self.col) |
232 entity = self.entity(self.row, self.col) |
273 eschema = self.vreg.schema.eschema(entity.name) |
233 eschema = self.vreg.schema.eschema(entity.name) |
274 visitor = OneHopESchemaVisitor(self.req, eschema, skiprels=self.skip_rels) |
234 skipmeta = not int(self.req.form.get('withmeta', 0)) |
275 s2d.schema2dot(outputfile=tmpfile, visitor=visitor, |
235 visitor = OneHopESchemaVisitor(self.req, eschema, |
276 prophdlr=RestrictedSchemaDotPropsHandler(self.req)) |
236 skiptypes=skipmeta and SKIP_TYPES or ()) |
|
237 s2d.schema2dot(outputfile=tmpfile, visitor=visitor) |
277 |
238 |
278 class CWRTypeSchemaImageView(CWETypeSchemaImageView): |
239 class CWRTypeSchemaImageView(CWETypeSchemaImageView): |
279 __select__ = implements('CWRType') |
240 __select__ = implements('CWRType') |
280 |
241 |
281 def _generate(self, tmpfile): |
242 def _generate(self, tmpfile): |
282 """display schema information for an entity""" |
243 """display schema information for an entity""" |
283 entity = self.entity(self.row, self.col) |
244 entity = self.entity(self.row, self.col) |
284 rschema = self.vreg.schema.rschema(entity.name) |
245 rschema = self.vreg.schema.rschema(entity.name) |
285 visitor = OneHopRSchemaVisitor(self.req, rschema) |
246 visitor = OneHopRSchemaVisitor(self.req, rschema) |
286 s2d.schema2dot(outputfile=tmpfile, visitor=visitor, |
247 s2d.schema2dot(outputfile=tmpfile, visitor=visitor) |
287 prophdlr=RestrictedSchemaDotPropsHandler(self.req)) |
|
288 |
248 |
289 ### facets |
249 ### facets |
290 |
250 |
291 class CWMetaFacet(AttributeFacet): |
251 class CWMetaFacet(AttributeFacet): |
292 id = 'cwmeta-facet' |
252 id = 'cwmeta-facet' |