web/views/schema.py
changeset 5233 673b63953e7a
parent 5232 78c1a531f7b3
child 5234 e2476d78b060
equal deleted inserted replaced
5232:78c1a531f7b3 5233:673b63953e7a
     7 """
     7 """
     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.common.ureports import Section, Table
    12 from logilab.mtconverter import xml_escape
    13 from logilab.mtconverter import xml_escape
    13 from yams import BASE_TYPES, schema2dot as s2d
    14 from yams import BASE_TYPES, schema2dot as s2d
    14 from yams.buildobjs import DEFAULT_ATTRPERMS
    15 from yams.buildobjs import DEFAULT_ATTRPERMS
    15 
    16 
    16 from cubicweb.selectors import (implements, yes, match_user_groups,
    17 from cubicweb.selectors import (implements, yes, match_user_groups,
    17                                 has_related_entities)
    18                                 has_related_entities, anonymous_user)
    18 from cubicweb.schema import (META_RTYPES, SCHEMA_TYPES, SYSTEM_RTYPES,
    19 from cubicweb.schema import (META_RTYPES, SCHEMA_TYPES, SYSTEM_RTYPES,
    19                              WORKFLOW_TYPES, INTERNAL_TYPES)
    20                              WORKFLOW_TYPES, INTERNAL_TYPES)
    20 from cubicweb.schemaviewer import SchemaViewer
    21 from cubicweb.schemaviewer import SchemaViewer
    21 from cubicweb.view import EntityView, StartupView
    22 from cubicweb.view import EntityView, StartupView
    22 from cubicweb import tags, uilib
    23 from cubicweb import tags, uilib
    23 from cubicweb.web import action, facet, uicfg
    24 from cubicweb.web import action, facet, uicfg
    24 from cubicweb.web.views import TmpFileViewMixin
    25 from cubicweb.web.views import TmpFileViewMixin
    25 from cubicweb.web.views import primary, baseviews, tabs, management
    26 from cubicweb.web.views import primary, baseviews, tabs, management, tableview
    26 
    27 
    27 ALWAYS_SKIP_TYPES = BASE_TYPES | SCHEMA_TYPES
    28 ALWAYS_SKIP_TYPES = BASE_TYPES | SCHEMA_TYPES
    28 SKIP_TYPES  = (ALWAYS_SKIP_TYPES | META_RTYPES | SYSTEM_RTYPES | WORKFLOW_TYPES
    29 SKIP_TYPES  = (ALWAYS_SKIP_TYPES | META_RTYPES | SYSTEM_RTYPES | WORKFLOW_TYPES
    29                | INTERNAL_TYPES)
    30                | INTERNAL_TYPES)
    30 SKIP_TYPES.update(set(('CWUser', 'CWGroup')))
    31 SKIP_TYPES.update(set(('CWUser', 'CWGroup')))
   187                         rdef.subject, rschema, rdef.object))
   188                         rdef.subject, rschema, rdef.object))
   188                 self.schema_definition(rdef)
   189                 self.schema_definition(rdef)
   189             self.w(u'</div>')
   190             self.w(u'</div>')
   190 
   191 
   191 
   192 
   192 class SchemaUreportsView(StartupView):
       
   193     __regid__ = 'schema-block'
       
   194 
       
   195     def call(self):
       
   196         viewer = SchemaViewer(self._cw)
       
   197         layout = viewer.visit_schema(self._cw.vreg.schema, display_relations=True,
       
   198                                      skiptypes=skip_types(self._cw))
       
   199         self.w(uilib.ureport_as_html(layout))
       
   200 
       
   201 
       
   202 # CWAttribute / CWRelation #####################################################
   193 # CWAttribute / CWRelation #####################################################
   203 
   194 
   204 class CWRDEFPrimaryView(primary.PrimaryView):
   195 class CWRDEFPrimaryView(primary.PrimaryView):
   205     __select__ = implements('CWAttribute', 'CWRelation')
   196     __select__ = implements('CWAttribute', 'CWRelation')
   206     cache_max_age = 60*60*2 # stay in http cache for 2 hours by default
   197     cache_max_age = 60*60*2 # stawy in http cache for 2 hours by default
   207 
   198 
   208     def render_entity_title(self, entity):
   199     def render_entity_title(self, entity):
   209         self.w(u'<h1><span class="etype">%s</span> %s</h1>'
   200         self.w(u'<h1><span class="etype">%s</span> %s</h1>'
   210                % (entity.dc_type().capitalize(),
   201                % (entity.dc_type().capitalize(),
   211                   xml_escape(entity.dc_long_title())))
   202                   xml_escape(entity.dc_long_title())))
   212 
   203 
       
   204 
       
   205 class CWAttributeConstraints(EntityView):
       
   206     __regid__ = 'attr_constraints'
       
   207     __select__ = implements('CWAttribute')
       
   208 
       
   209     def cell_call(self, row, col):
       
   210         entity = self.cw_rset.get_entity(row, col)
       
   211         rschema = self._cw.vreg.schema.rschema(entity.rtype.name)
       
   212         rdef = rschema.rdefs[(entity.stype.name, entity.otype.name)]
       
   213         constraints = [xml_escape(str(c)) for c in getattr(rdef, 'constraints')]
       
   214         self.w(u', '.join(constraints))
       
   215 
       
   216 
       
   217 class CWAttributeCellView(tableview.CellView):
       
   218     """display attribute name only in a cell view, link to
       
   219        cwattribute primary view"""
       
   220     __select__ = tableview.CellView.__select__ & implements('CWAttribute')
       
   221 
       
   222     def cell_call(self, row, col, cellvid=None):
       
   223         """display attribute name only in a cell view, link to
       
   224            cwattribute primary view"""
       
   225         etype, val = self.cw_rset.description[row][col], self.cw_rset[row][col]
       
   226         if val is not None and not self._cw.vreg.schema.eschema(etype).final:
       
   227             e = self.cw_rset.get_entity(row, col)
       
   228             if cellvid is not None and cellvid != 'incontext':
       
   229                 self.wview(cellvid, e.as_rset(), 'null')
       
   230             else:
       
   231                 desc = uilib.cut(e.dc_description(), 50)
       
   232                 self.w(u'<a href="%s" title="%s">%s' % 
       
   233                        (xml_escape(e.absolute_url()) ,xml_escape(desc),
       
   234                         self.cell_content(e)))
       
   235                 self.w(u'</a>')
       
   236         elif val is None:
       
   237             # This is usually caused by a left outer join and in that case,
       
   238             # regular views will most certainly fail if they don't have
       
   239             # a real eid
       
   240             self.wview('final', self.cw_rset, row=row, col=col)
       
   241         else:
       
   242             self.wview(cellvid or 'final', self.cw_rset, 'null', row=row, col=col)
       
   243 
       
   244     def cell_content(self, entity):
       
   245         return entity.relation_type[0].name
       
   246 
       
   247 
       
   248 class CWRelationCellView(CWAttributeCellView):
       
   249     """display relation name and its translation only in a cell view, link to
       
   250        cwrelation primary view"""
       
   251     __select__ = tableview.CellView.__select__ & implements('CWRelation')
       
   252 
       
   253     def cell_content(self, entity):
       
   254         return u'%s (%s)' % (entity.relation_type[0].name,
       
   255                              self._cw._(entity.relation_type[0].name))
   213 
   256 
   214 # CWEType ######################################################################
   257 # CWEType ######################################################################
   215 
   258 
   216 class CWETypeOneLineView(baseviews.OneLineView):
   259 class CWETypeOneLineView(baseviews.OneLineView):
   217     __select__ = implements('CWEType')
   260     __select__ = implements('CWEType')
   224         super(CWETypeOneLineView, self).cell_call(row, col, **kwargs)
   267         super(CWETypeOneLineView, self).cell_call(row, col, **kwargs)
   225         if final:
   268         if final:
   226             self.w(u'</em>')
   269             self.w(u'</em>')
   227 
   270 
   228 
   271 
   229 class CWETypePrimaryView(tabs.TabsMixin, primary.PrimaryView):
   272 class CWETypePrimaryView(tabs.TabbedPrimaryView):
   230     __select__ = implements('CWEType')
   273     __select__ = implements('CWEType')
   231     title = _('in memory entity schema')
   274     tabs = [_('cwetype-text-tab'), _('cwetype-box-tab'), _('cwetype-workflow-tab'),
   232     main_related_section = False
   275             _('cwetype-views-tab'), _('cwetype-perm-tab')]
   233     tabs = [_('cwetype-schema-text'), _('cwetype-schema-image'),
   276     default_tab = 'cwetype-text-tab'
   234             _('cwetype-schema-permissions'), _('cwetype-workflow')]
   277 
   235     default_tab = 'cwetype-schema-text'
   278 
   236 
   279 # register generated msgid
   237     def render_entity(self, entity):
   280 _('i18ncard_1'), _('i18ncard_?'), _('i18ncard_+'), _('i18ncard_*')
   238         self.render_entity_title(entity)
   281 
   239         self.w(u'<div>%s</div>' % entity.description)
   282 class CWETypeTextTab(tabs.PrimaryTab):
   240         self.render_tabs(self.tabs, self.default_tab, entity)
   283     __regid__ = 'cwetype-text-tab'
   241 
   284     __select__ = tabs.PrimaryTab.__select__ & implements('CWEType')
   242 
   285 
   243 class CWETypeSTextView(EntityView):
   286     def render_entity_attributes(self, entity, siderelations=None):
   244     __regid__ = 'cwetype-schema-text'
   287         self.w(u'<div>%s</div>' % xml_escape(entity.description or u''))
   245     __select__ = EntityView.__select__ & implements('CWEType')
   288         # entity schema image
   246 
   289         url = entity.absolute_url(vid='schemagraph')
   247     def cell_call(self, row, col):
   290         self.w(u'<img src="%s" alt="%s"/>' % (
   248         entity = self.cw_rset.get_entity(row, col)
   291             xml_escape(url),
       
   292             xml_escape(self._cw._('graphical schema for %s') % entity.name)))
       
   293         # entity schema attributes
   249         self.w(u'<h2>%s</h2>' % _('Attributes'))
   294         self.w(u'<h2>%s</h2>' % _('Attributes'))
   250         rset = self._cw.execute('Any N,F,D,I,J,DE,A '
   295         rset = self._cw.execute('Any A,F,DE,D,C, I,J,A '
   251                                 'ORDERBY AA WHERE A is CWAttribute, '
   296                                 'ORDERBY AA WHERE A is CWAttribute, '
   252                                 'A ordernum AA, A defaultval D, '
   297                                 'A ordernum AA, A defaultval D, '
   253                                 'A description DE, '
   298                                 'A description DE, A cardinality C, '
   254                                 'A fulltextindexed I, A internationalizable J, '
   299                                 'A fulltextindexed I, A internationalizable J, '
   255                                 'A relation_type R, R name N, '
   300                                 'A relation_type R, R name N, '
   256                                 'A to_entity O, O name F, '
   301                                 'A to_entity O, O name F, '
   257                                 'A from_entity S, S eid %(x)s',
   302                                 'A from_entity S, S eid %(x)s',
   258                                 {'x': entity.eid})
   303                                 {'x': entity.eid})
   259         self.wview('editable-table', rset, 'null', displayfilter=True)
   304         self.wview('table', rset, 'null',
       
   305                    cellvids={4: 'attr_cardinality', 7: 'attr_constraints'},
       
   306                    headers=(_(u'name'), _(u'type'), _(u'description'),
       
   307                             _(u'default value'), _(u'required'),
       
   308                             _(u'fulltext indexed'), _(u'internationalizable'),
       
   309                             _(u'constraints')),
       
   310                    mainindex=0)
       
   311         # entity schema relations
   260         self.w(u'<h2>%s</h2>' % _('Relations'))
   312         self.w(u'<h2>%s</h2>' % _('Relations'))
   261         rset = self._cw.execute(
   313         rset = self._cw.execute(
   262             'Any R,C,TT,K,D,A,RN,TTN ORDERBY RN '
   314             'Any A, TT, "i18ncard_"+SUBSTRING(C, 1, 1),K,D,A,RN,TTN ORDERBY RN '
   263             'WHERE A is CWRelation, A description D, A composite K, '
   315             'WHERE A is CWRelation, A description D, A composite K, '
   264             'A relation_type R, R name RN, A to_entity TT, TT name TTN, '
   316             'A relation_type R, R name RN, A to_entity TT, TT name TTN, '
   265             'A cardinality C, A from_entity S, S eid %(x)s',
   317             'A cardinality C, A from_entity S, S eid %(x)s',
   266             {'x': entity.eid})
   318             {'x': entity.eid})
   267         self.wview('editable-table', rset, 'null', displayfilter=True,
   319         if rset:
   268                    displaycols=range(6), mainindex=5)
   320             self.w(u'<h5>%s %s</h5>' % (entity.name, _('is subject of:')))
       
   321         self.wview('table', rset, 'null',
       
   322                    cellvids={2: 'entity_relation_cardinality',},
       
   323                    headers=(_(u'name'), _(u'object type'), _(u'cardinality'),
       
   324                             _(u'composite'), _(u'description'),
       
   325                             _(u'relation direction')),
       
   326                    displaycols=range(5), mainindex=0)
       
   327         self.w(u'<br/>')
   269         rset = self._cw.execute(
   328         rset = self._cw.execute(
   270             'Any R,C,TT,K,D,A,RN,TTN ORDERBY RN '
   329             'Any A, TT, "i18ncard_"+SUBSTRING(C, 2, 1),K,D,A,RN,TTN ORDERBY RN '
   271             'WHERE A is CWRelation, A description D, A composite K, '
   330             'WHERE A is CWRelation, A description D, A composite K, '
   272             'A relation_type R, R name RN, A from_entity TT, TT name TTN, '
   331             'A relation_type R, R name RN, A from_entity TT, TT name TTN, '
   273             'A cardinality C, A to_entity O, O eid %(x)s',
   332             'A cardinality C, A to_entity O, O eid %(x)s',
   274             {'x': entity.eid})
   333             {'x': entity.eid})
   275         self.wview('editable-table', rset, 'null', displayfilter=True,
   334         if rset:
   276                    displaycols=range(6), mainindex=5)
   335             self.w(u'<h5>%s %s</h5>' % (entity.name, _('is object of:')))
   277 
   336         self.wview('table', rset, 'null',
   278 
   337                    cellvids={2: 'entity_relation_cardinality',},
   279 class CWETypeSImageView(EntityView):
   338                    headers=(_(u'name'), _(u'subject type'), _(u'cardinality'),
   280     __regid__ = 'cwetype-schema-image'
   339                             _(u'composite'), _(u'description'),
   281     __select__ = EntityView.__select__ & implements('CWEType')
   340                             _(u'relation direction')),
   282 
   341                    displaycols=range(5), mainindex=0)
   283     def cell_call(self, row, col):
   342 
       
   343 
       
   344 class CWETypePermTab(EntityView, management.SecurityViewMixIn):
       
   345     __regid__ = 'cwetype-perm-tab'
       
   346     __select__ = EntityView.__select__ & implements('CWEType') & ~anonymous_user()
       
   347 
       
   348     def cell_call(self, row, col):
       
   349         self._cw.add_css('cubicweb.acl.css')
   284         entity = self.cw_rset.get_entity(row, col)
   350         entity = self.cw_rset.get_entity(row, col)
   285         url = entity.absolute_url(vid='schemagraph')
   351         eschema = self._cw.vreg.schema.eschema(entity.name)
   286         self.w(u'<img src="%s" alt="%s"/>' % (
   352         self.w(u'<a id="%s" href="cwetype/%s">' %  (eschema.type, eschema.type))
   287             xml_escape(url),
   353         self.w(u'<h3 class="schema">%s (%s) ' % (eschema.type, _(eschema.type)))
   288             xml_escape(self._cw._('graphical schema for %s') % entity.name)))
   354         self.w(u'</h3></a>')
   289 
   355         self.w(u'<div style="margin: 0px 1.5em">')
   290 
   356         self.schema_definition(eschema)
   291 class CWETypeSPermView(EntityView):
   357         # display entity attributes only if they have some permissions modified
   292     __regid__ = 'cwetype-schema-permissions'
   358         modified_attrs = []
   293     __select__ = EntityView.__select__ & implements('CWEType')
   359         self.w(u'<h4>%s</h4>' % _('attributes permissions:').capitalize())
   294 
   360         for attr, etype in  eschema.attribute_definitions():
   295     def cell_call(self, row, col):
   361             if attr not in META_RTYPES:
   296         entity = self.cw_rset.get_entity(row, col)
   362                 rdef = eschema.rdef(attr)
   297         _ = self._cw._
   363                 attrtype = str(rdef.rtype)
   298         self.w(u'<h2>%s</h2>' % _('Add permissions'))
   364                 self.w(u'<h4 class="schema">%s (%s)</h4> ' % (attrtype, _(attrtype)))
   299         rset = self._cw.execute('Any P WHERE X add_permission P, '
   365                 self.schema_definition(rdef, link=False)
   300                                 'X eid %(x)s',
   366         self.w(u'</div>')
   301                                 {'x': entity.eid})
   367 
   302         self.wview('outofcontext', rset, 'null')
   368 
   303         self.w(u'<h2>%s</h2>' % _('Read permissions'))
   369 class CWETypeWorkflowTab(EntityView):
   304         rset = self._cw.execute('Any P WHERE X read_permission P, '
   370     __regid__ = 'cwetype-workflow-tab'
   305                                 'X eid %(x)s',
       
   306                                 {'x': entity.eid})
       
   307         self.wview('outofcontext', rset, 'null')
       
   308         self.w(u'<h2>%s</h2>' % _('Update permissions'))
       
   309         rset = self._cw.execute('Any P WHERE X update_permission P, '
       
   310                                 'X eid %(x)s',
       
   311                                 {'x': entity.eid})
       
   312         self.wview('outofcontext', rset, 'null')
       
   313         self.w(u'<h2>%s</h2>' % _('Delete permissions'))
       
   314         rset = self._cw.execute('Any P WHERE X delete_permission P, '
       
   315                                 'X eid %(x)s',
       
   316                                 {'x': entity.eid})
       
   317         self.wview('outofcontext', rset, 'null')
       
   318 
       
   319 
       
   320 class CWETypeSWorkflowView(EntityView):
       
   321     __regid__ = 'cwetype-workflow'
       
   322     __select__ = (EntityView.__select__ & implements('CWEType') &
   371     __select__ = (EntityView.__select__ & implements('CWEType') &
   323                   has_related_entities('workflow_of', 'object'))
   372                   has_related_entities('workflow_of', 'object'))
   324 
   373 
   325     def cell_call(self, row, col):
   374     def cell_call(self, row, col):
   326         entity = self.cw_rset.get_entity(row, col)
   375         entity = self.cw_rset.get_entity(row, col)
   345         self.w(wf.view('wfgraph'))
   394         self.w(wf.view('wfgraph'))
   346         self.w('<a href="%s">%s</a>' % (
   395         self.w('<a href="%s">%s</a>' % (
   347             wf.absolute_url(), self._cw._('more info about this workflow')))
   396             wf.absolute_url(), self._cw._('more info about this workflow')))
   348 
   397 
   349 
   398 
       
   399 class CWETypeBoxTab(EntityView):
       
   400     __regid__ = 'cwetype-box-tab'
       
   401     __select__ = EntityView.__select__ & implements('CWEType')
       
   402 
       
   403     def cell_call(self, row, col):
       
   404         viewer = SchemaViewer(self._cw)
       
   405         entity = self.cw_rset.get_entity(row, col)
       
   406         eschema = self._cw.vreg.schema.eschema(entity.name)
       
   407         layout = viewer.visit_entityschema(eschema)
       
   408         self.w(uilib.ureport_as_html(layout))
       
   409         self.w(u'<br class="clear"/>')
       
   410 
       
   411 
       
   412 class CWETypeViewsTab(EntityView):
       
   413     __regid__ = 'cwetype-views-tab'
       
   414     __select__ = EntityView.__select__ & implements('CWEType')
       
   415 
       
   416     def cell_call(self, row, col):
       
   417         entity = self.cw_rset.get_entity(row, col)
       
   418         etype = entity.name
       
   419         _ = self._cw._
       
   420         # possible views for this entity type
       
   421         views = [(_(view.title),) for view in self.possible_views(etype)]
       
   422         self.wview('pyvaltable', pyvalue=views, headers=(_(u'views'),))
       
   423 
       
   424     def possible_views(self, etype):
       
   425         rset = self._cw.etype_rset(etype)
       
   426         return [v for v in self._cw.vreg['views'].possible_views(self._cw, rset)
       
   427                 if v.category != 'startupview']
       
   428 
       
   429 
   350 # CWRType ######################################################################
   430 # CWRType ######################################################################
   351 
   431 
   352 class CWRTypeSchemaView(primary.PrimaryView):
   432 class CWRTypeSchemaView(primary.PrimaryView):
   353     __select__ = implements('CWRType')
   433     __select__ = implements('CWRType')
   354     title = _('in memory relation schema')
   434     title = _('in memory relation schema')
   363         if not rschema.final:
   443         if not rschema.final:
   364             msg = self._cw._('graphical schema for %s') % entity.name
   444             msg = self._cw._('graphical schema for %s') % entity.name
   365             self.w(tags.img(src=entity.absolute_url(vid='schemagraph'),
   445             self.w(tags.img(src=entity.absolute_url(vid='schemagraph'),
   366                             alt=msg))
   446                             alt=msg))
   367 
   447 
   368 
       
   369 # schema images ###############################################################
   448 # schema images ###############################################################
   370 
   449 
   371 class RestrictedSchemaVisitorMixIn(object):
   450 class RestrictedSchemaVisitorMixIn(object):
   372     def __init__(self, req, *args, **kwargs):
   451     def __init__(self, req, *args, **kwargs):
   373         self._cw = req
   452         self._cw = req
   428         rschema = self._cw.vreg.schema.rschema(entity.name)
   507         rschema = self._cw.vreg.schema.rschema(entity.name)
   429         visitor = OneHopRSchemaVisitor(self._cw, rschema)
   508         visitor = OneHopRSchemaVisitor(self._cw, rschema)
   430         s2d.schema2dot(outputfile=tmpfile, visitor=visitor)
   509         s2d.schema2dot(outputfile=tmpfile, visitor=visitor)
   431 
   510 
   432 
   511 
       
   512 # final views #################################################################
       
   513 
       
   514 class CardinalityRequiredCellView(baseviews.FinalView):
       
   515     __regid__ = 'attr_cardinality'
       
   516 
       
   517     def cell_call(self, row, col):
       
   518         value = self.cw_rset.rows[row][col]
       
   519         if value is not None and value[0] == '1':
       
   520             self.w(self._cw._(u'required'))
       
   521 
       
   522 class I18NCardinalityCellView(baseviews.FinalView):
       
   523     __regid__ = 'entity_relation_cardinality'
       
   524 
       
   525     def cell_call(self, row, col):
       
   526         value = self.cw_rset.rows[row][col]
       
   527         if value is not None:
       
   528             self.w(self._cw._(value))
       
   529 
       
   530 
   433 # misc: facets, actions ########################################################
   531 # misc: facets, actions ########################################################
   434 
   532 
   435 class CWFinalFacet(facet.AttributeFacet):
   533 class CWFinalFacet(facet.AttributeFacet):
   436     __regid__ = 'cwfinal-facet'
   534     __regid__ = 'cwfinal-facet'
   437     __select__ = facet.AttributeFacet.__select__ & implements('CWEType', 'CWRType')
   535     __select__ = facet.AttributeFacet.__select__ & implements('CWEType', 'CWRType')