|
1 """Specific views for schema related entities |
|
2 |
|
3 :organization: Logilab |
|
4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
|
5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
6 """ |
|
7 __docformat__ = "restructuredtext en" |
|
8 |
|
9 from itertools import cycle |
|
10 |
|
11 from logilab.mtconverter import html_escape |
|
12 from logilab.common.graph import escape, GraphGenerator, DotBackend |
|
13 from yams import schema2dot as s2d |
|
14 |
|
15 from cubicweb.selectors import implements, rql_condition, yes |
|
16 from cubicweb.schemaviewer import SchemaViewer |
|
17 from cubicweb.view import EntityView, StartupView |
|
18 from cubicweb.common.uilib import ureport_as_html |
|
19 from cubicweb.web.action import Action |
|
20 from cubicweb.web.views import baseviews |
|
21 from cubicweb.web.views import TmpFileViewMixin |
|
22 |
|
23 |
|
24 class ViewSchemaAction(Action): |
|
25 id = 'schema' |
|
26 __select__ = yes() |
|
27 |
|
28 title = _("site schema") |
|
29 category = 'siteactions' |
|
30 order = 30 |
|
31 |
|
32 def url(self): |
|
33 return self.build_url(self.id) |
|
34 |
|
35 |
|
36 # schema entity types views ################################################### |
|
37 |
|
38 class _SchemaEntityPrimaryView(baseviews.PrimaryView): |
|
39 show_attr_label = False |
|
40 cache_max_age = 60*60*2 # stay in http cache for 2 hours by default |
|
41 |
|
42 def content_title(self, entity): |
|
43 return html_escape(entity.dc_long_title()) |
|
44 |
|
45 class EETypePrimaryView(_SchemaEntityPrimaryView): |
|
46 __select__ = implements('EEType') |
|
47 skip_attrs = _SchemaEntityPrimaryView.skip_attrs + ('name', 'meta', 'final') |
|
48 |
|
49 class ERTypePrimaryView(_SchemaEntityPrimaryView): |
|
50 __select__ = implements('ERType') |
|
51 skip_attrs = _SchemaEntityPrimaryView.skip_attrs + ('name', 'meta', 'final', |
|
52 'symetric', 'inlined') |
|
53 |
|
54 class ErdefPrimaryView(_SchemaEntityPrimaryView): |
|
55 __select__ = implements('EFRDef', 'ENFRDef') |
|
56 show_attr_label = True |
|
57 |
|
58 class EETypeOneLineView(baseviews.OneLineView): |
|
59 __select__ = implements('EEType') |
|
60 |
|
61 def cell_call(self, row, col, **kwargs): |
|
62 entity = self.entity(row, col) |
|
63 final = entity.final |
|
64 if final: |
|
65 self.w(u'<em class="finalentity">') |
|
66 super(EETypeOneLineView, self).cell_call(row, col, **kwargs) |
|
67 if final: |
|
68 self.w(u'</em>') |
|
69 |
|
70 |
|
71 # in memory schema views (yams class instances) ############################### |
|
72 |
|
73 class EETypeSchemaView(EETypePrimaryView): |
|
74 id = 'eschema' |
|
75 title = _('in memory entity schema') |
|
76 main_related_section = False |
|
77 skip_rels = ('is', 'is_instance_of', 'identity', 'created_by', 'owned_by', |
|
78 'has_text',) |
|
79 |
|
80 def render_entity_attributes(self, entity, siderelations): |
|
81 super(EETypeSchemaView, self).render_entity_attributes(entity, siderelations) |
|
82 eschema = self.vreg.schema.eschema(entity.name) |
|
83 viewer = SchemaViewer(self.req) |
|
84 layout = viewer.visit_entityschema(eschema, skiprels=self.skip_rels) |
|
85 self.w(ureport_as_html(layout)) |
|
86 if not eschema.is_final(): |
|
87 self.w(u'<img src="%s" alt="%s"/>' % ( |
|
88 html_escape(entity.absolute_url(vid='eschemagraph')), |
|
89 html_escape(self.req._('graphical schema for %s') % entity.name))) |
|
90 |
|
91 |
|
92 class ERTypeSchemaView(ERTypePrimaryView): |
|
93 id = 'eschema' |
|
94 title = _('in memory relation schema') |
|
95 main_related_section = False |
|
96 |
|
97 def render_entity_attributes(self, entity, siderelations): |
|
98 super(ERTypeSchemaView, self).render_entity_attributes(entity, siderelations) |
|
99 rschema = self.vreg.schema.rschema(entity.name) |
|
100 viewer = SchemaViewer(self.req) |
|
101 layout = viewer.visit_relationschema(rschema) |
|
102 self.w(ureport_as_html(layout)) |
|
103 if not rschema.is_final(): |
|
104 self.w(u'<img src="%s" alt="%s"/>' % ( |
|
105 html_escape(entity.absolute_url(vid='eschemagraph')), |
|
106 html_escape(self.req._('graphical schema for %s') % entity.name))) |
|
107 |
|
108 |
|
109 # schema images ############################################################### |
|
110 |
|
111 class ImageView(EntityView): |
|
112 __select__ = implements('EEType') |
|
113 id = 'image' |
|
114 title = _('image') |
|
115 |
|
116 def cell_call(self, row, col): |
|
117 entity = self.entity(row, col) |
|
118 url = entity.absolute_url(vid='eschemagraph') |
|
119 self.w(u'<img src="%s" alt="%s"/>' % ( |
|
120 html_escape(url), |
|
121 html_escape(self.req._('graphical schema for %s') % entity.name))) |
|
122 |
|
123 |
|
124 class RestrictedSchemaDotPropsHandler(s2d.SchemaDotPropsHandler): |
|
125 def __init__(self, req): |
|
126 # FIXME: colors are arbitrary |
|
127 self.nextcolor = cycle( ('#aa0000', '#00aa00', '#0000aa', |
|
128 '#000000', '#888888') ).next |
|
129 self.req = req |
|
130 |
|
131 def display_attr(self, rschema): |
|
132 return not rschema.meta and (rschema.has_local_role('read') |
|
133 or rschema.has_perm(self.req, 'read')) |
|
134 |
|
135 # XXX remove this method once yams > 0.20 is out |
|
136 def node_properties(self, eschema): |
|
137 """return default DOT drawing options for an entity schema""" |
|
138 label = ['{',eschema.type,'|'] |
|
139 label.append(r'\l'.join(rel.type for rel in eschema.subject_relations() |
|
140 if rel.final and self.display_attr(rel))) |
|
141 label.append(r'\l}') # trailing \l ensure alignement of the last one |
|
142 return {'label' : ''.join(label), 'shape' : "record", |
|
143 'fontname' : "Courier", 'style' : "filled"} |
|
144 |
|
145 def edge_properties(self, rschema, subjnode, objnode): |
|
146 kwargs = super(RestrictedSchemaDotPropsHandler, self).edge_properties(rschema, subjnode, objnode) |
|
147 # symetric rels are handled differently, let yams decide what's best |
|
148 if not rschema.symetric: |
|
149 kwargs['color'] = self.nextcolor() |
|
150 kwargs['fontcolor'] = kwargs['color'] |
|
151 # dot label decoration is just awful (1 line underlining the label |
|
152 # + 1 line going to the closest edge spline point) |
|
153 kwargs['decorate'] = 'false' |
|
154 return kwargs |
|
155 |
|
156 |
|
157 class RestrictedSchemaVisitorMiIn: |
|
158 def __init__(self, req, *args, **kwargs): |
|
159 # hack hack hack |
|
160 assert len(self.__class__.__bases__) == 2 |
|
161 self.__parent = self.__class__.__bases__[1] |
|
162 self.__parent.__init__(self, *args, **kwargs) |
|
163 self.req = req |
|
164 |
|
165 def nodes(self): |
|
166 for etype, eschema in self.__parent.nodes(self): |
|
167 if eschema.has_local_role('read') or eschema.has_perm(self.req, 'read'): |
|
168 yield eschema.type, eschema |
|
169 |
|
170 def edges(self): |
|
171 for setype, oetype, rschema in self.__parent.edges(self): |
|
172 if rschema.has_local_role('read') or rschema.has_perm(self.req, 'read'): |
|
173 yield setype, oetype, rschema |
|
174 |
|
175 |
|
176 class FullSchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.FullSchemaVisitor): |
|
177 pass |
|
178 |
|
179 class OneHopESchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.OneHopESchemaVisitor): |
|
180 pass |
|
181 |
|
182 class OneHopRSchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.OneHopRSchemaVisitor): |
|
183 pass |
|
184 |
|
185 |
|
186 class SchemaImageView(TmpFileViewMixin, StartupView): |
|
187 id = 'schemagraph' |
|
188 content_type = 'image/png' |
|
189 skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of') |
|
190 def _generate(self, tmpfile): |
|
191 """display global schema information""" |
|
192 skipmeta = not int(self.req.form.get('withmeta', 0)) |
|
193 visitor = FullSchemaVisitor(self.req, self.schema, skiprels=self.skip_rels, skipmeta=skipmeta) |
|
194 s2d.schema2dot(outputfile=tmpfile, visitor=visitor, |
|
195 prophdlr=RestrictedSchemaDotPropsHandler(self.req)) |
|
196 |
|
197 class EETypeSchemaImageView(TmpFileViewMixin, EntityView): |
|
198 id = 'eschemagraph' |
|
199 content_type = 'image/png' |
|
200 __select__ = implements('EEType') |
|
201 skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of') |
|
202 |
|
203 def _generate(self, tmpfile): |
|
204 """display schema information for an entity""" |
|
205 entity = self.entity(self.row, self.col) |
|
206 eschema = self.vreg.schema.eschema(entity.name) |
|
207 visitor = OneHopESchemaVisitor(self.req, eschema, skiprels=self.skip_rels) |
|
208 s2d.schema2dot(outputfile=tmpfile, visitor=visitor, |
|
209 prophdlr=RestrictedSchemaDotPropsHandler(self.req)) |
|
210 |
|
211 class ERTypeSchemaImageView(EETypeSchemaImageView): |
|
212 __select__ = implements('ERType') |
|
213 |
|
214 def _generate(self, tmpfile): |
|
215 """display schema information for an entity""" |
|
216 entity = self.entity(self.row, self.col) |
|
217 rschema = self.vreg.schema.rschema(entity.name) |
|
218 visitor = OneHopRSchemaVisitor(self.req, rschema) |
|
219 s2d.schema2dot(outputfile=tmpfile, visitor=visitor, |
|
220 prophdlr=RestrictedSchemaDotPropsHandler(self.req)) |