web/views/schema.py
changeset 2436 44b2eea35efa
parent 2387 ea1defea9636
child 2476 1294a6bdf3bf
equal deleted inserted replaced
2435:85be7a811afe 2436:44b2eea35efa
     8 __docformat__ = "restructuredtext en"
     8 __docformat__ = "restructuredtext en"
     9 
     9 
    10 from itertools import cycle
    10 from itertools import cycle
    11 
    11 
    12 from logilab.mtconverter import xml_escape
    12 from logilab.mtconverter import xml_escape
    13 from yams import schema2dot as s2d
    13 from yams import BASE_TYPES, schema2dot as s2d
    14 
    14 
    15 from cubicweb.selectors import implements, yes
    15 from cubicweb.selectors import implements, yes, match_user_groups
    16 from cubicweb.schema import META_RELATIONS_TYPES, SCHEMA_TYPES
    16 from cubicweb.schema import META_RELATIONS_TYPES, SCHEMA_TYPES
    17 from cubicweb.schemaviewer import SchemaViewer
    17 from cubicweb.schemaviewer import SchemaViewer
    18 from cubicweb.view import EntityView, StartupView
    18 from cubicweb.view import EntityView, StartupView
    19 from cubicweb.common import tags, uilib
    19 from cubicweb.common import tags, uilib
    20 from cubicweb.web import action
    20 from cubicweb.web import action, facet
    21 from cubicweb.web.views import TmpFileViewMixin, primary, baseviews, tabs
    21 from cubicweb.web.views import TmpFileViewMixin
    22 from cubicweb.web.facet import AttributeFacet
    22 from cubicweb.web.views import primary, baseviews, tabs, management
    23 
    23 
    24 SKIP_TYPES = set()
    24 ALWAYS_SKIP_TYPES = BASE_TYPES | SCHEMA_TYPES
    25 SKIP_TYPES.update(META_RELATIONS_TYPES)
    25 SKIP_TYPES = ALWAYS_SKIP_TYPES | META_RELATIONS_TYPES
    26 SKIP_TYPES.update(SCHEMA_TYPES)
    26 SKIP_TYPES.update(set(('Transition', 'State', 'TrInfo',
       
    27                        'CWUser', 'CWGroup',
       
    28                        'CWCache', 'CWProperty', 'CWPermission',
       
    29                        'ExternalUri')))
    27 
    30 
    28 def skip_types(req):
    31 def skip_types(req):
    29     if int(req.form.get('skipmeta', True)):
    32     if int(req.form.get('skipmeta', True)):
    30         return SKIP_TYPES
    33         return SKIP_TYPES
    31     return ()
    34     return ALWAYS_SKIP_TYPES
    32 
    35 
    33 class ViewSchemaAction(action.Action):
    36 # global schema view ###########################################################
       
    37 
       
    38 class SchemaView(tabs.TabsMixin, StartupView):
    34     id = 'schema'
    39     id = 'schema'
    35     __select__ = yes()
    40     title = _('application schema')
    36 
    41     tabs = [_('schema-text'), _('schema-image')]
    37     title = _("site schema")
    42     default_tab = 'schema-text'
    38     category = 'siteactions'
    43 
    39     order = 30
    44     def call(self):
    40 
    45         """display schema information"""
    41     def url(self):
    46         self.req.add_js('cubicweb.ajax.js')
    42         return self.build_url(self.id)
    47         self.req.add_css(('cubicweb.schema.css','cubicweb.acl.css'))
    43 
    48         self.w(u'<h1>%s</h1>' % _('Schema of the data model'))
       
    49         self.render_tabs(self.tabs, self.default_tab)
       
    50 
       
    51 
       
    52 class SchemaTabImageView(StartupView):
       
    53     id = 'schema-image'
       
    54 
       
    55     def call(self):
       
    56         self.w(_(u'<div>This schema of the data model <em>excludes</em> the '
       
    57                  u'meta-data, but you can also display a <a href="%s">complete '
       
    58                  u'schema with meta-data</a>.</div>')
       
    59                % xml_escape(self.build_url('view', vid='schemagraph', skipmeta=0)))
       
    60         self.w(u'<img src="%s" alt="%s"/>\n' % (
       
    61             xml_escape(self.req.build_url('view', vid='schemagraph', skipmeta=1)),
       
    62             self.req._("graphical representation of the application'schema")))
       
    63 
       
    64 
       
    65 class SchemaTabTextView(StartupView):
       
    66     id = 'schema-text'
       
    67 
       
    68     def call(self):
       
    69         rset = self.req.execute('Any X ORDERBY N WHERE X is CWEType, X name N, '
       
    70                                 'X final FALSE')
       
    71         self.wview('table', rset, displayfilter=True)
       
    72 
       
    73 
       
    74 class ManagerSchemaPermissionsView(StartupView, management.SecurityViewMixIn):
       
    75     id = 'schema-security'
       
    76     __select__ = StartupView.__select__ & match_user_groups('managers')
       
    77 
       
    78     def call(self, display_relations=True):
       
    79         self.req.add_css('cubicweb.acl.css')
       
    80         skiptypes = skip_types(self.req)
       
    81         formparams = {}
       
    82         formparams['sec'] = self.id
       
    83         if not skiptypes:
       
    84             formparams['skipmeta'] = u'0'
       
    85         schema = self.schema
       
    86         # compute entities
       
    87         entities = sorted(eschema for eschema in schema.entities()
       
    88                           if not (eschema.is_final() or eschema in skiptypes))
       
    89         # compute relations
       
    90         if display_relations:
       
    91             relations = sorted(rschema for rschema in schema.relations()
       
    92                                if not (rschema.is_final()
       
    93                                        or rschema in skiptypes
       
    94                                        or rschema in META_RELATIONS_TYPES))
       
    95         else:
       
    96             relations = []
       
    97         # index
       
    98         _ = self.req._
       
    99         self.w(u'<div id="schema_security"><a id="index" href="index"/>')
       
   100         self.w(u'<h2 class="schema">%s</h2>' % _('index').capitalize())
       
   101         self.w(u'<h4>%s</h4>' %   _('Entities').capitalize())
       
   102         ents = []
       
   103         for eschema in sorted(entities):
       
   104             url = xml_escape(self.build_url('schema', **formparams))
       
   105             ents.append(u'<a class="grey" href="%s#%s">%s</a> (%s)' % (
       
   106                 url,  eschema.type, eschema.type, _(eschema.type)))
       
   107         self.w(u', '.join(ents))
       
   108         self.w(u'<h4>%s</h4>' % (_('relations').capitalize()))
       
   109         rels = []
       
   110         for rschema in sorted(relations):
       
   111             url = xml_escape(self.build_url('schema', **formparams))
       
   112             rels.append(u'<a class="grey" href="%s#%s">%s</a> (%s), ' %  (
       
   113                 url , rschema.type, rschema.type, _(rschema.type)))
       
   114         self.w(u', '.join(ents))
       
   115         # entities
       
   116         self.display_entities(entities, formparams)
       
   117         # relations
       
   118         if relations:
       
   119             self.display_relations(relations, formparams)
       
   120         self.w(u'</div>')
       
   121 
       
   122     def display_entities(self, entities, formparams):
       
   123         _ = self.req._
       
   124         self.w(u'<a id="entities" href="entities"/>')
       
   125         self.w(u'<h2 class="schema">%s</h2>' % _('permissions for entities').capitalize())
       
   126         for eschema in entities:
       
   127             self.w(u'<a id="%s" href="%s"/>' %  (eschema.type, eschema.type))
       
   128             self.w(u'<h3 class="schema">%s (%s) ' % (eschema.type, _(eschema.type)))
       
   129             url = xml_escape(self.build_url('schema', **formparams) + '#index')
       
   130             self.w(u'<a href="%s"><img src="%s" alt="%s"/></a>' % (
       
   131                 url,  self.req.external_resource('UP_ICON'), _('up')))
       
   132             self.w(u'</h3>')
       
   133             self.w(u'<div style="margin: 0px 1.5em">')
       
   134             self.schema_definition(eschema, link=False)
       
   135             # display entity attributes only if they have some permissions modified
       
   136             modified_attrs = []
       
   137             for attr, etype in  eschema.attribute_definitions():
       
   138                 if self.has_schema_modified_permissions(attr, attr.ACTIONS):
       
   139                     modified_attrs.append(attr)
       
   140             if  modified_attrs:
       
   141                 self.w(u'<h4>%s</h4>' % _('attributes with modified permissions:').capitalize())
       
   142                 self.w(u'</div>')
       
   143                 self.w(u'<div style="margin: 0px 6em">')
       
   144                 for attr in  modified_attrs:
       
   145                     self.w(u'<h4 class="schema">%s (%s)</h4> ' % (attr.type, _(attr.type)))
       
   146                     self.schema_definition(attr, link=False)
       
   147             self.w(u'</div>')
       
   148 
       
   149     def display_relations(self, relations, formparams):
       
   150         _ = self.req._
       
   151         self.w(u'<a id="relations" href="relations"/>')
       
   152         self.w(u'<h2 class="schema">%s </h2>' % _('permissions for relations').capitalize())
       
   153         for rschema in relations:
       
   154             self.w(u'<a id="%s" href="%s"/>' %  (rschema.type, rschema.type))
       
   155             self.w(u'<h3 class="schema">%s (%s) ' % (rschema.type, _(rschema.type)))
       
   156             url = xml_escape(self.build_url('schema', **formparams) + '#index')
       
   157             self.w(u'<a href="%s"><img src="%s" alt="%s"/></a>' % (
       
   158                 url,  self.req.external_resource('UP_ICON'), _('up')))
       
   159             self.w(u'</h3>')
       
   160             self.w(u'<div style="margin: 0px 1.5em">')
       
   161             subjects = [str(subj) for subj in rschema.subjects()]
       
   162             self.w(u'<div><strong>%s</strong> %s (%s)</div>' % (
       
   163                 _('subject_plural:'),
       
   164                 ', '.join(str(subj) for subj in rschema.subjects()),
       
   165                 ', '.join(_(str(subj)) for subj in rschema.subjects())))
       
   166             self.w(u'<div><strong>%s</strong> %s (%s)</div>' % (
       
   167                 _('object_plural:'),
       
   168                 ', '.join(str(obj) for obj in rschema.objects()),
       
   169                 ', '.join(_(str(obj)) for obj in rschema.objects())))
       
   170             self.schema_definition(rschema, link=False)
       
   171             self.w(u'</div>')
       
   172 
       
   173 
       
   174 class SchemaUreportsView(StartupView):
       
   175     id = 'schema-block'
       
   176 
       
   177     def call(self):
       
   178         viewer = SchemaViewer(self.req)
       
   179         layout = viewer.visit_schema(self.schema, display_relations=True,
       
   180                                      skiptypes=skip_types(self.req))
       
   181         self.w(uilib.ureport_as_html(layout))
       
   182 
       
   183 
       
   184 # CWAttribute / CWRelation #####################################################
    44 
   185 
    45 class CWRDEFPrimaryView(primary.PrimaryView):
   186 class CWRDEFPrimaryView(primary.PrimaryView):
    46     __select__ = implements('CWAttribute', 'CWRelation')
   187     __select__ = implements('CWAttribute', 'CWRelation')
    47     cache_max_age = 60*60*2 # stay in http cache for 2 hours by default
   188     cache_max_age = 60*60*2 # stay in http cache for 2 hours by default
    48 
   189 
   194         self.req = req
   335         self.req = req
   195         super(RestrictedSchemaVisitorMixIn, self).__init__(*args, **kwargs)
   336         super(RestrictedSchemaVisitorMixIn, self).__init__(*args, **kwargs)
   196 
   337 
   197     def should_display_schema(self, rschema):
   338     def should_display_schema(self, rschema):
   198         return (super(RestrictedSchemaVisitorMixIn, self).should_display_schema(rschema)
   339         return (super(RestrictedSchemaVisitorMixIn, self).should_display_schema(rschema)
   199                 and rschema.has_local_role('read') or rschema.has_perm(self.req, 'read'))
   340                 and (rschema.has_local_role('read')
       
   341                      or rschema.has_perm(self.req, 'read')))
   200 
   342 
   201     def should_display_attr(self, rschema):
   343     def should_display_attr(self, rschema):
   202         return (super(RestrictedSchemaVisitorMixIn, self).should_display_attr(rschema)
   344         return (super(RestrictedSchemaVisitorMixIn, self).should_display_attr(rschema)
   203                 and rschema.has_local_role('read') or rschema.has_perm(self.req, 'read'))
   345                 and (rschema.has_local_role('read')
       
   346                      or rschema.has_perm(self.req, 'read')))
   204 
   347 
   205 
   348 
   206 class FullSchemaVisitor(RestrictedSchemaVisitorMixIn, s2d.FullSchemaVisitor):
   349 class FullSchemaVisitor(RestrictedSchemaVisitorMixIn, s2d.FullSchemaVisitor):
   207     pass
   350     pass
   208 
   351 
   219     id = 'schemagraph'
   362     id = 'schemagraph'
   220     content_type = 'image/png'
   363     content_type = 'image/png'
   221 
   364 
   222     def _generate(self, tmpfile):
   365     def _generate(self, tmpfile):
   223         """display global schema information"""
   366         """display global schema information"""
       
   367         print 'skipedtypes', skip_types(self.req)
   224         visitor = FullSchemaVisitor(self.req, self.schema,
   368         visitor = FullSchemaVisitor(self.req, self.schema,
   225                                     skiptypes=skip_types(self.req))
   369                                     skiptypes=skip_types(self.req))
   226         s2d.schema2dot(outputfile=tmpfile, visitor=visitor)
   370         s2d.schema2dot(outputfile=tmpfile, visitor=visitor)
       
   371 
   227 
   372 
   228 class CWETypeSchemaImageView(TmpFileViewMixin, EntityView):
   373 class CWETypeSchemaImageView(TmpFileViewMixin, EntityView):
   229     id = 'schemagraph'
   374     id = 'schemagraph'
   230     __select__ = implements('CWEType')
   375     __select__ = implements('CWEType')
   231     content_type = 'image/png'
   376     content_type = 'image/png'
   236         eschema = self.vreg.schema.eschema(entity.name)
   381         eschema = self.vreg.schema.eschema(entity.name)
   237         visitor = OneHopESchemaVisitor(self.req, eschema,
   382         visitor = OneHopESchemaVisitor(self.req, eschema,
   238                                        skiptypes=skip_types(self.req))
   383                                        skiptypes=skip_types(self.req))
   239         s2d.schema2dot(outputfile=tmpfile, visitor=visitor)
   384         s2d.schema2dot(outputfile=tmpfile, visitor=visitor)
   240 
   385 
       
   386 
   241 class CWRTypeSchemaImageView(CWETypeSchemaImageView):
   387 class CWRTypeSchemaImageView(CWETypeSchemaImageView):
   242     __select__ = implements('CWRType')
   388     __select__ = implements('CWRType')
   243 
   389 
   244     def _generate(self, tmpfile):
   390     def _generate(self, tmpfile):
   245         """display schema information for an entity"""
   391         """display schema information for an entity"""
   246         entity = self.entity(self.row, self.col)
   392         entity = self.entity(self.row, self.col)
   247         rschema = self.vreg.schema.rschema(entity.name)
   393         rschema = self.vreg.schema.rschema(entity.name)
   248         visitor = OneHopRSchemaVisitor(self.req, rschema)
   394         visitor = OneHopRSchemaVisitor(self.req, rschema)
   249         s2d.schema2dot(outputfile=tmpfile, visitor=visitor)
   395         s2d.schema2dot(outputfile=tmpfile, visitor=visitor)
   250 
   396 
   251 ### facets
   397 
   252 
   398 # misc: facets, actions ########################################################
   253 class CWFinalFacet(AttributeFacet):
   399 
       
   400 class CWFinalFacet(facet.AttributeFacet):
   254     id = 'cwfinal-facet'
   401     id = 'cwfinal-facet'
   255     __select__ = AttributeFacet.__select__ & implements('CWEType', 'CWRType')
   402     __select__ = facet.AttributeFacet.__select__ & implements('CWEType', 'CWRType')
   256     rtype = 'final'
   403     rtype = 'final'
       
   404 
       
   405 class ViewSchemaAction(action.Action):
       
   406     id = 'schema'
       
   407     __select__ = yes()
       
   408 
       
   409     title = _("site schema")
       
   410     category = 'siteactions'
       
   411     order = 30
       
   412 
       
   413     def url(self):
       
   414         return self.build_url(self.id)