web/views/schema.py
changeset 2126 a25859917ccc
parent 1998 12040c090aa4
child 2181 94ca417b9b07
equal deleted inserted replaced
2123:3e1d2ab5f8c0 2126:a25859917ccc
     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'