web/views/schema.py
changeset 5234 e2476d78b060
parent 5233 673b63953e7a
child 5235 3a92e6d92a64
equal deleted inserted replaced
5233:673b63953e7a 5234:e2476d78b060
    13 from logilab.mtconverter import xml_escape
    13 from logilab.mtconverter import xml_escape
    14 from yams import BASE_TYPES, schema2dot as s2d
    14 from yams import BASE_TYPES, schema2dot as s2d
    15 from yams.buildobjs import DEFAULT_ATTRPERMS
    15 from yams.buildobjs import DEFAULT_ATTRPERMS
    16 
    16 
    17 from cubicweb.selectors import (implements, yes, match_user_groups,
    17 from cubicweb.selectors import (implements, yes, match_user_groups,
    18                                 has_related_entities, anonymous_user)
    18                                 has_related_entities, authenticated_user)
    19 from cubicweb.schema import (META_RTYPES, SCHEMA_TYPES, SYSTEM_RTYPES,
    19 from cubicweb.schema import (META_RTYPES, SCHEMA_TYPES, SYSTEM_RTYPES,
    20                              WORKFLOW_TYPES, INTERNAL_TYPES)
    20                              WORKFLOW_TYPES, INTERNAL_TYPES)
    21 from cubicweb.schemaviewer import SchemaViewer
    21 from cubicweb.schemaviewer import SchemaViewer
    22 from cubicweb.view import EntityView, StartupView
    22 from cubicweb.view import EntityView, StartupView
    23 from cubicweb import tags, uilib
    23 from cubicweb import tags, uilib
    24 from cubicweb.web import action, facet, uicfg
    24 from cubicweb.web import action, facet, uicfg
    25 from cubicweb.web.views import TmpFileViewMixin
    25 from cubicweb.web.views import TmpFileViewMixin
    26 from cubicweb.web.views import primary, baseviews, tabs, management, tableview
    26 from cubicweb.web.views import primary, baseviews, tabs, tableview, iprogress
    27 
    27 
    28 ALWAYS_SKIP_TYPES = BASE_TYPES | SCHEMA_TYPES
    28 ALWAYS_SKIP_TYPES = BASE_TYPES | SCHEMA_TYPES
    29 SKIP_TYPES  = (ALWAYS_SKIP_TYPES | META_RTYPES | SYSTEM_RTYPES | WORKFLOW_TYPES
    29 SKIP_TYPES  = (ALWAYS_SKIP_TYPES | META_RTYPES | SYSTEM_RTYPES | WORKFLOW_TYPES
    30                | INTERNAL_TYPES)
    30                | INTERNAL_TYPES)
    31 SKIP_TYPES.update(set(('CWUser', 'CWGroup')))
    31 SKIP_TYPES.update(set(('CWUser', 'CWGroup')))
    38 _pvs = uicfg.primaryview_section
    38 _pvs = uicfg.primaryview_section
    39 for _action in ('read', 'add', 'update', 'delete'):
    39 for _action in ('read', 'add', 'update', 'delete'):
    40     _pvs.tag_subject_of(('*', '%s_permission' % _action, '*'), 'hidden')
    40     _pvs.tag_subject_of(('*', '%s_permission' % _action, '*'), 'hidden')
    41     _pvs.tag_object_of(('*', '%s_permission' % _action, '*'), 'hidden')
    41     _pvs.tag_object_of(('*', '%s_permission' % _action, '*'), 'hidden')
    42 
    42 
       
    43 _pvs.tag_object_of(('Workflow', 'workflow_of', 'CWEType'), 'hidden')
       
    44 _pvs.tag_subject_of(('CWEType', 'default_workflow', 'Workflow'), 'hidden')
       
    45 
       
    46 _pvs.tag_object_of(('*', 'relation_type', 'CWRType'), 'hidden')
       
    47 
       
    48 
       
    49 class SecurityViewMixIn(object):
       
    50     """mixin providing methods to display security information for a entity,
       
    51     relation or relation definition schema
       
    52     """
       
    53 
       
    54     def permissions_table(self, erschema, permissions=None):
       
    55         self._cw.add_css('cubicweb.acl.css')
       
    56         w = self.w
       
    57         _ = self._cw._
       
    58         w(u'<table class="schemaInfo">')
       
    59         w(u'<tr><th>%s</th><th>%s</th><th>%s</th></tr>' % (
       
    60             _("permission"), _('granted to groups'), _('rql expressions')))
       
    61         w(u'<table class="schemaInfo">')
       
    62         w(u'<tr><th>%s</th><th>%s</th><th>%s</th></tr>\n' %
       
    63                 (_('permission'), _('granted to groups'),
       
    64                 _('rql expressions')))
       
    65         for action in erschema.ACTIONS:
       
    66             w(u'<tr><td>%s</td><td>' % _(action))
       
    67             if permissions is None:
       
    68                 groups = erschema.get_groups(action)
       
    69             else:
       
    70                 groups = permissions[action][1]
       
    71             # XXX get group entity and call it's incontext view
       
    72             groups = [u'<a class="%s" href="%s">%s</a>' % (
       
    73                 group, self._cw.build_url('cwgroup/%s' % group), label)
       
    74                       for group, label in sorted((_(g), g) for group in groups)]
       
    75             w(u'<br/>'.join(groups))
       
    76             w(u'</td><td>')
       
    77             if permissions is None:
       
    78                 rqlexprs = sorted(e.expression for e in erschema.get_rqlexprs(action))
       
    79             else:
       
    80                 rqlexprs = permissions[action][1]
       
    81             w(u'<br/>'.join(rqlexprs))
       
    82             w(u'</td></tr>\n')
       
    83         w(u'</table>')
       
    84 
       
    85     def grouped_permissions_table(self, rschema):
       
    86         # group relation definitions with identical permissions
       
    87         perms = {}
       
    88         for rdef in rschema.rdefs.itervalues():
       
    89             rdef_perms = []
       
    90             for action in ('read', 'add', 'delete'):
       
    91                 groups = sorted(rdef.get_groups(action))
       
    92                 exprs = sorted(e.expression for e in rdef.get_rqlexprs(action))
       
    93                 rdef_perms.append( (action, (tuple(groups), tuple(exprs))) )
       
    94             rdef_perms = tuple(rdef_perms)
       
    95             if rdef_perms in perms:
       
    96                 perms[rdef_perms].append( (rdef.subject, rdef.object) )
       
    97             else:
       
    98                 perms[rdef_perms] = [(rdef.subject, rdef.object)]
       
    99         # set layout permissions in a table for each group of relation
       
   100         # definition
       
   101         w = self.w
       
   102         w(u'<div style="margin: 0px 1.5em">')
       
   103         tmpl = u'[<strong>%s</strong> %s <strong>%s</strong>]'
       
   104         for perm, rdefs in perms:
       
   105             w(u'<div>%s</div>' % u', '.join(
       
   106                 tmpl % (_(s.type), _(rschema.type), _(o.type)) for s, o in rdefs))
       
   107             # accessing rdef from previous loop by design: only used to get
       
   108             # ACTIONS
       
   109             self.permissions_table(rdef, dict(perm))
       
   110         w(u'</div>')
       
   111 
       
   112 
    43 # global schema view ###########################################################
   113 # global schema view ###########################################################
    44 
   114 
    45 class SchemaView(tabs.TabsMixin, StartupView):
   115 class SchemaView(tabs.TabsMixin, StartupView):
    46     __regid__ = 'schema'
   116     __regid__ = 'schema'
    47     title = _('instance schema')
   117     title = _('instance schema')
    48     tabs = [_('schema-text'), _('schema-image'), _('schema-security')]
   118     tabs = [_('schema-description'), _('schema-image'), _('schema-security')]
    49     default_tab = 'schema-text'
   119     default_tab = 'schema-description'
    50 
   120 
    51     def call(self):
   121     def call(self):
    52         """display schema information"""
   122         """display schema information"""
    53         self._cw.add_js('cubicweb.ajax.js')
       
    54         self._cw.add_css(('cubicweb.schema.css','cubicweb.acl.css'))
       
    55         self.w(u'<h1>%s</h1>' % _('Schema of the data model'))
   123         self.w(u'<h1>%s</h1>' % _('Schema of the data model'))
    56         self.render_tabs(self.tabs, self.default_tab)
   124         self.render_tabs(self.tabs, self.default_tab)
    57 
   125 
    58 
   126 
    59 class SchemaTabImageView(StartupView):
   127 class SchemaImageTab(StartupView):
    60     __regid__ = 'schema-image'
   128     __regid__ = 'schema-image'
    61 
   129 
    62     def call(self):
   130     def call(self):
    63         self.w(_(u'<div>This schema of the data model <em>excludes</em> the '
   131         self.w(_(u'<div>This schema of the data model <em>excludes</em> the '
    64                  u'meta-data, but you can also display a <a href="%s">complete '
   132                  u'meta-data, but you can also display a <a href="%s">complete '
    67         self.w(u'<img src="%s" alt="%s"/>\n' % (
   135         self.w(u'<img src="%s" alt="%s"/>\n' % (
    68             xml_escape(self._cw.build_url('view', vid='schemagraph', skipmeta=1)),
   136             xml_escape(self._cw.build_url('view', vid='schemagraph', skipmeta=1)),
    69             self._cw._("graphical representation of the instance'schema")))
   137             self._cw._("graphical representation of the instance'schema")))
    70 
   138 
    71 
   139 
    72 class SchemaTabTextView(StartupView):
   140 class SchemaDescriptionTab(StartupView):
    73     __regid__ = 'schema-text'
   141     __regid__ = 'schema-description'
    74 
   142 
    75     def call(self):
   143     def call(self):
    76         rset = self._cw.execute('Any X ORDERBY N WHERE X is CWEType, X name N, '
   144         rset = self._cw.execute('Any X ORDERBY N WHERE X is CWEType, X name N, '
    77                                 'X final FALSE')
   145                                 'X final FALSE')
    78         self.wview('table', rset, displayfilter=True)
   146         self.wview('table', rset, displayfilter=True)
    82         owl_downloadurl = self._cw.build_url('view', vid='owl')
   150         owl_downloadurl = self._cw.build_url('view', vid='owl')
    83         self.w(u'<div><a href="%s">%s</a></div>' %
   151         self.w(u'<div><a href="%s">%s</a></div>' %
    84                (owl_downloadurl, self._cw._(u'Download schema as OWL')))
   152                (owl_downloadurl, self._cw._(u'Download schema as OWL')))
    85 
   153 
    86 
   154 
    87 class SchemaPermissionsView(StartupView, management.SecurityViewMixIn):
   155 class SchemaPermissionsTab(SecurityViewMixIn, StartupView):
    88     __regid__ = 'schema-security'
   156     __regid__ = 'schema-security'
    89     __select__ = StartupView.__select__ & match_user_groups('managers')
   157     __select__ = StartupView.__select__ & match_user_groups('managers')
    90 
   158 
    91     def call(self, display_relations=True):
   159     def call(self, display_relations=True):
    92         self._cw.add_css('cubicweb.acl.css')
   160         self._cw.add_css('cubicweb.acl.css')
   125         if relations:
   193         if relations:
   126             self.display_relations(relations)
   194             self.display_relations(relations)
   127         self.w(u'</div>')
   195         self.w(u'</div>')
   128 
   196 
   129     def has_non_default_perms(self, rdef):
   197     def has_non_default_perms(self, rdef):
   130         for access_type in rdef.ACTIONS:
   198         """return true if the given *attribute* relation definition has custom
       
   199         permission
       
   200         """
       
   201         for action in rdef.ACTIONS:
   131             def_rqlexprs = []
   202             def_rqlexprs = []
   132             def_groups = []
   203             def_groups = []
   133             for perm in DEFAULT_ATTRPERMS[access_type]:
   204             for perm in DEFAULT_ATTRPERMS[action]:
   134                 if not isinstance(perm, basestring):
   205                 if not isinstance(perm, basestring):
   135                     def_rqlexprs.append(perm.expression)
   206                     def_rqlexprs.append(perm.expression)
   136                 else:
   207                 else:
   137                     def_groups.append(perm)
   208                     def_groups.append(perm)
   138             rqlexprs = [rql.expression for rql in rdef.get_rqlexprs(access_type)]
   209             rqlexprs = [rql.expression for rql in rdef.get_rqlexprs(action)]
   139             groups = rdef.get_groups(access_type)
   210             groups = rdef.get_groups(action)
   140             if groups != frozenset(def_groups) or \
   211             if groups != frozenset(def_groups) or \
   141                 frozenset(rqlexprs) != frozenset(def_rqlexprs):
   212                 frozenset(rqlexprs) != frozenset(def_rqlexprs):
   142                 return True
   213                 return True
   143         return False
   214         return False
   144 
   215 
   152                 eschema.type, _(eschema.type)))
   223                 eschema.type, _(eschema.type)))
   153             self.w(u'<a href="%s#schema_security"><img src="%s" alt="%s"/></a>' % (
   224             self.w(u'<a href="%s#schema_security"><img src="%s" alt="%s"/></a>' % (
   154                 url,  self._cw.external_resource('UP_ICON'), _('up')))
   225                 url,  self._cw.external_resource('UP_ICON'), _('up')))
   155             self.w(u'</h3>')
   226             self.w(u'</h3>')
   156             self.w(u'<div style="margin: 0px 1.5em">')
   227             self.w(u'<div style="margin: 0px 1.5em">')
   157             self.schema_definition(eschema)
   228             self.permissions_table(eschema)
   158             # display entity attributes only if they have some permissions modified
   229             # display entity attributes only if they have some permissions modified
   159             modified_attrs = []
   230             modified_attrs = []
   160             for attr, etype in  eschema.attribute_definitions():
   231             for attr, etype in  eschema.attribute_definitions():
   161                 rdef = eschema.rdef(attr)
   232                 rdef = eschema.rdef(attr)
   162                 if attr not in META_RTYPES and self.has_non_default_perms(rdef):
   233                 if attr not in META_RTYPES and self.has_non_default_perms(rdef):
   166                 self.w(u'</div>')
   237                 self.w(u'</div>')
   167                 self.w(u'<div style="margin: 0px 6em">')
   238                 self.w(u'<div style="margin: 0px 6em">')
   168                 for rdef in modified_attrs:
   239                 for rdef in modified_attrs:
   169                     attrtype = str(rdef.rtype)
   240                     attrtype = str(rdef.rtype)
   170                     self.w(u'<h4 class="schema">%s (%s)</h4> ' % (attrtype, _(attrtype)))
   241                     self.w(u'<h4 class="schema">%s (%s)</h4> ' % (attrtype, _(attrtype)))
   171                     self.schema_definition(rdef)
   242                     self.permissions_table(rdef)
   172             self.w(u'</div>')
   243             self.w(u'</div>')
   173 
   244 
   174     def display_relations(self, relations):
   245     def display_relations(self, relations):
   175         _ = self._cw._
   246         _ = self._cw._
   176         url = xml_escape(self._cw.build_url('schema'))
   247         url = xml_escape(self._cw.build_url('schema'))
   180                 rschema.type, self._cw.build_url('cwrtype/%s' % rschema.type),
   251                 rschema.type, self._cw.build_url('cwrtype/%s' % rschema.type),
   181                 rschema.type, _(rschema.type)))
   252                 rschema.type, _(rschema.type)))
   182             self.w(u'<a href="%s#schema_security"><img src="%s" alt="%s"/></a>' % (
   253             self.w(u'<a href="%s#schema_security"><img src="%s" alt="%s"/></a>' % (
   183                 url,  self._cw.external_resource('UP_ICON'), _('up')))
   254                 url,  self._cw.external_resource('UP_ICON'), _('up')))
   184             self.w(u'</h3>')
   255             self.w(u'</h3>')
   185             self.w(u'<div style="margin: 0px 1.5em">')
   256             self.grouped_permissions_table(rschema)
   186             for rdef in rschema.rdefs.itervalues():
   257 
   187                 self.w(u'<h4 class="schema">%s %s %s</h4>' % (
       
   188                         rdef.subject, rschema, rdef.object))
       
   189                 self.schema_definition(rdef)
       
   190             self.w(u'</div>')
       
   191 
       
   192 
       
   193 # CWAttribute / CWRelation #####################################################
       
   194 
       
   195 class CWRDEFPrimaryView(primary.PrimaryView):
       
   196     __select__ = implements('CWAttribute', 'CWRelation')
       
   197     cache_max_age = 60*60*2 # stawy in http cache for 2 hours by default
       
   198 
       
   199     def render_entity_title(self, entity):
       
   200         self.w(u'<h1><span class="etype">%s</span> %s</h1>'
       
   201                % (entity.dc_type().capitalize(),
       
   202                   xml_escape(entity.dc_long_title())))
       
   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))
       
   256 
   258 
   257 # CWEType ######################################################################
   259 # CWEType ######################################################################
   258 
   260 
   259 class CWETypeOneLineView(baseviews.OneLineView):
   261 # register msgid generated in entity relations tables
   260     __select__ = implements('CWEType')
   262 _('i18ncard_1'), _('i18ncard_?'), _('i18ncard_+'), _('i18ncard_*')
   261 
       
   262     def cell_call(self, row, col, **kwargs):
       
   263         entity = self.cw_rset.get_entity(row, col)
       
   264         final = entity.final
       
   265         if final:
       
   266             self.w(u'<em class="finalentity">')
       
   267         super(CWETypeOneLineView, self).cell_call(row, col, **kwargs)
       
   268         if final:
       
   269             self.w(u'</em>')
       
   270 
       
   271 
   263 
   272 class CWETypePrimaryView(tabs.TabbedPrimaryView):
   264 class CWETypePrimaryView(tabs.TabbedPrimaryView):
   273     __select__ = implements('CWEType')
   265     __select__ = implements('CWEType')
   274     tabs = [_('cwetype-text-tab'), _('cwetype-box-tab'), _('cwetype-workflow-tab'),
   266     tabs = [_('cwetype-description'), _('cwetype-box'), _('cwetype-workflow'),
   275             _('cwetype-views-tab'), _('cwetype-perm-tab')]
   267             _('cwetype-views'), _('cwetype-permissions')]
   276     default_tab = 'cwetype-text-tab'
   268     default_tab = 'cwetype-description'
   277 
   269 
   278 
   270 
   279 # register generated msgid
   271 class CWETypeDescriptionTab(tabs.PrimaryTab):
   280 _('i18ncard_1'), _('i18ncard_?'), _('i18ncard_+'), _('i18ncard_*')
   272     __regid__ = 'cwetype-description'
   281 
       
   282 class CWETypeTextTab(tabs.PrimaryTab):
       
   283     __regid__ = 'cwetype-text-tab'
       
   284     __select__ = tabs.PrimaryTab.__select__ & implements('CWEType')
   273     __select__ = tabs.PrimaryTab.__select__ & implements('CWEType')
   285 
   274 
   286     def render_entity_attributes(self, entity, siderelations=None):
   275     def render_entity_attributes(self, entity, siderelations=None):
   287         self.w(u'<div>%s</div>' % xml_escape(entity.description or u''))
   276         self.w(u'<div>%s</div>' % xml_escape(entity.description or u''))
   288         # entity schema image
   277         # entity schema image
   290         self.w(u'<img src="%s" alt="%s"/>' % (
   279         self.w(u'<img src="%s" alt="%s"/>' % (
   291             xml_escape(url),
   280             xml_escape(url),
   292             xml_escape(self._cw._('graphical schema for %s') % entity.name)))
   281             xml_escape(self._cw._('graphical schema for %s') % entity.name)))
   293         # entity schema attributes
   282         # entity schema attributes
   294         self.w(u'<h2>%s</h2>' % _('Attributes'))
   283         self.w(u'<h2>%s</h2>' % _('Attributes'))
   295         rset = self._cw.execute('Any A,F,DE,D,C, I,J,A '
   284         rset = self._cw.execute(
   296                                 'ORDERBY AA WHERE A is CWAttribute, '
   285             'Any A,F,DE,D,C, I,J,A ORDERBY AA WHERE A is CWAttribute, '
   297                                 'A ordernum AA, A defaultval D, '
   286             'A ordernum AA, A defaultval D, A description DE, A cardinality C, '
   298                                 'A description DE, A cardinality C, '
   287             'A fulltextindexed I, A internationalizable J, '
   299                                 'A fulltextindexed I, A internationalizable J, '
   288             'A relation_type R, R name N, A to_entity O, O name F, '
   300                                 'A relation_type R, R name N, '
   289             'A from_entity S, S eid %(x)s',
   301                                 'A to_entity O, O name F, '
   290             {'x': entity.eid})
   302                                 'A from_entity S, S eid %(x)s',
       
   303                                 {'x': entity.eid})
       
   304         self.wview('table', rset, 'null',
   291         self.wview('table', rset, 'null',
   305                    cellvids={4: 'attr_cardinality', 7: 'attr_constraints'},
   292                    cellvids={0: 'rdef-name-cell',
       
   293                              4: 'etype-attr-cardinality-cell',
       
   294                              7: 'rdef-constraints-cell'},
   306                    headers=(_(u'name'), _(u'type'), _(u'description'),
   295                    headers=(_(u'name'), _(u'type'), _(u'description'),
   307                             _(u'default value'), _(u'required'),
   296                             _(u'default value'), _(u'required'),
   308                             _(u'fulltext indexed'), _(u'internationalizable'),
   297                             _(u'fulltext indexed'), _(u'internationalizable'),
   309                             _(u'constraints')),
   298                             _(u'constraints')),
   310                    mainindex=0)
   299                    mainindex=0)
   311         # entity schema relations
   300         # entity schema relations
   312         self.w(u'<h2>%s</h2>' % _('Relations'))
   301         self.w(u'<h2>%s</h2>' % _('Relations'))
   313         rset = self._cw.execute(
   302         rset = self._cw.execute(
   314             'Any A, TT, "i18ncard_"+SUBSTRING(C, 1, 1),K,D,A,RN,TTN ORDERBY RN '
   303             'Any A, TT, "i18ncard_"+SUBSTRING(C, 1, 1),K,D,A,A,RN,TTN ORDERBY RN '
   315             'WHERE A is CWRelation, A description D, A composite K, '
   304             'WHERE A is CWRelation, A description D, A composite K, '
   316             'A relation_type R, R name RN, A to_entity TT, TT name TTN, '
   305             'A relation_type R, R name RN, A to_entity TT, TT name TTN, '
   317             'A cardinality C, A from_entity S, S eid %(x)s',
   306             'A cardinality C, A from_entity S, S eid %(x)s',
   318             {'x': entity.eid})
   307             {'x': entity.eid})
   319         if rset:
   308         if rset:
   320             self.w(u'<h5>%s %s</h5>' % (entity.name, _('is subject of:')))
   309             self.w(u'<h5>%s %s</h5>' % (entity.name, _('is subject of:')))
   321         self.wview('table', rset, 'null',
   310         self.wview('table', rset, 'null',
   322                    cellvids={2: 'entity_relation_cardinality',},
   311                    cellvids={0: 'rdef-name-cell',
       
   312                              2: 'etype-rel-cardinality-cell',
       
   313                              5: 'rdef-constraints-cell'},
   323                    headers=(_(u'name'), _(u'object type'), _(u'cardinality'),
   314                    headers=(_(u'name'), _(u'object type'), _(u'cardinality'),
   324                             _(u'composite'), _(u'description'),
   315                             _(u'composite'), _(u'description'),
   325                             _(u'relation direction')),
   316                             _(u'constraints'), _(u'relation direction')),
   326                    displaycols=range(5), mainindex=0)
   317                    displaycols=range(5), mainindex=0)
   327         self.w(u'<br/>')
   318         self.w(u'<br/>')
   328         rset = self._cw.execute(
   319         rset = self._cw.execute(
   329             'Any A, TT, "i18ncard_"+SUBSTRING(C, 2, 1),K,D,A,RN,TTN ORDERBY RN '
   320             'Any A, TT, "i18ncard_"+SUBSTRING(C, 2, 1),K,D,A,A,RN,TTN ORDERBY RN '
   330             'WHERE A is CWRelation, A description D, A composite K, '
   321             'WHERE A is CWRelation, A description D, A composite K, '
   331             'A relation_type R, R name RN, A from_entity TT, TT name TTN, '
   322             'A relation_type R, R name RN, A from_entity TT, TT name TTN, '
   332             'A cardinality C, A to_entity O, O eid %(x)s',
   323             'A cardinality C, A to_entity O, O eid %(x)s',
   333             {'x': entity.eid})
   324             {'x': entity.eid})
   334         if rset:
   325         if rset:
   335             self.w(u'<h5>%s %s</h5>' % (entity.name, _('is object of:')))
   326             self.w(u'<h5>%s %s</h5>' % (entity.name, _('is object of:')))
   336         self.wview('table', rset, 'null',
   327         self.wview('table', rset, 'null',
   337                    cellvids={2: 'entity_relation_cardinality',},
   328                    cellvids={0: 'rdef-object-name-cell',
       
   329                              2: 'etype-rel-cardinality-cell',
       
   330                              5: 'rdef-constraints-cell'},
   338                    headers=(_(u'name'), _(u'subject type'), _(u'cardinality'),
   331                    headers=(_(u'name'), _(u'subject type'), _(u'cardinality'),
   339                             _(u'composite'), _(u'description'),
   332                             _(u'composite'), _(u'description'),
   340                             _(u'relation direction')),
   333                             _(u'constraints'), _(u'relation direction')),
   341                    displaycols=range(5), mainindex=0)
   334                    displaycols=range(5), mainindex=0)
   342 
   335 
   343 
   336 
   344 class CWETypePermTab(EntityView, management.SecurityViewMixIn):
   337 class CWETypeAttributeCardinalityCell(baseviews.FinalView):
   345     __regid__ = 'cwetype-perm-tab'
   338     __regid__ = 'etype-attr-cardinality-cell'
   346     __select__ = EntityView.__select__ & implements('CWEType') & ~anonymous_user()
   339 
       
   340     def cell_call(self, row, col):
       
   341         if self.cw_rset.rows[row][col][0] == '1':
       
   342             self.w(self._cw._(u'required'))
       
   343 
       
   344 
       
   345 class CWETypeRelationCardinalityCell(baseviews.FinalView):
       
   346     __regid__ = 'etype-rel-cardinality-cell'
       
   347 
       
   348     def cell_call(self, row, col):
       
   349         self.w(self._cw._(self.cw_rset.rows[row][col]))
       
   350 
       
   351 
       
   352 class CWETypeBoxTab(EntityView):
       
   353     __regid__ = 'cwetype-box'
       
   354     __select__ = implements('CWEType')
       
   355 
       
   356     def cell_call(self, row, col):
       
   357         viewer = SchemaViewer(self._cw)
       
   358         entity = self.cw_rset.get_entity(row, col)
       
   359         eschema = self._cw.vreg.schema.eschema(entity.name)
       
   360         layout = viewer.visit_entityschema(eschema)
       
   361         self.w(uilib.ureport_as_html(layout))
       
   362         self.w(u'<br class="clear"/>')
       
   363 
       
   364 
       
   365 class CWETypePermTab(SecurityViewMixIn, EntityView):
       
   366     __regid__ = 'cwetype-permissions'
       
   367     __select__ = implements('CWEType') & authenticated_user()
   347 
   368 
   348     def cell_call(self, row, col):
   369     def cell_call(self, row, col):
   349         self._cw.add_css('cubicweb.acl.css')
   370         self._cw.add_css('cubicweb.acl.css')
   350         entity = self.cw_rset.get_entity(row, col)
   371         entity = self.cw_rset.get_entity(row, col)
   351         eschema = self._cw.vreg.schema.eschema(entity.name)
   372         eschema = self._cw.vreg.schema.eschema(entity.name)
   352         self.w(u'<a id="%s" href="cwetype/%s">' %  (eschema.type, eschema.type))
       
   353         self.w(u'<h3 class="schema">%s (%s) ' % (eschema.type, _(eschema.type)))
       
   354         self.w(u'</h3></a>')
       
   355         self.w(u'<div style="margin: 0px 1.5em">')
   373         self.w(u'<div style="margin: 0px 1.5em">')
   356         self.schema_definition(eschema)
   374         self.permissions_table(eschema)
   357         # display entity attributes only if they have some permissions modified
       
   358         modified_attrs = []
       
   359         self.w(u'<h4>%s</h4>' % _('attributes permissions:').capitalize())
   375         self.w(u'<h4>%s</h4>' % _('attributes permissions:').capitalize())
   360         for attr, etype in  eschema.attribute_definitions():
   376         for attr, etype in  eschema.attribute_definitions():
   361             if attr not in META_RTYPES:
   377             if attr not in META_RTYPES:
   362                 rdef = eschema.rdef(attr)
   378                 rdef = eschema.rdef(attr)
   363                 attrtype = str(rdef.rtype)
   379                 attrtype = str(rdef.rtype)
   364                 self.w(u'<h4 class="schema">%s (%s)</h4> ' % (attrtype, _(attrtype)))
   380                 self.w(u'<h4 class="schema">%s (%s)</h4> ' % (attrtype, _(attrtype)))
   365                 self.schema_definition(rdef, link=False)
   381                 self.permissions_table(rdef)
   366         self.w(u'</div>')
   382         self.w(u'</div>')
   367 
   383 
   368 
   384 
   369 class CWETypeWorkflowTab(EntityView):
   385 class CWETypeWorkflowTab(EntityView):
   370     __regid__ = 'cwetype-workflow-tab'
   386     __regid__ = 'cwetype-workflow'
   371     __select__ = (EntityView.__select__ & implements('CWEType') &
   387     __select__ = (implements('CWEType')
   372                   has_related_entities('workflow_of', 'object'))
   388                   & has_related_entities('workflow_of', 'object'))
   373 
   389 
   374     def cell_call(self, row, col):
   390     def cell_call(self, row, col):
   375         entity = self.cw_rset.get_entity(row, col)
   391         entity = self.cw_rset.get_entity(row, col)
   376         if entity.default_workflow:
   392         if entity.default_workflow:
   377             wf = entity.default_workflow[0]
   393             wf = entity.default_workflow[0]
   394         self.w(wf.view('wfgraph'))
   410         self.w(wf.view('wfgraph'))
   395         self.w('<a href="%s">%s</a>' % (
   411         self.w('<a href="%s">%s</a>' % (
   396             wf.absolute_url(), self._cw._('more info about this workflow')))
   412             wf.absolute_url(), self._cw._('more info about this workflow')))
   397 
   413 
   398 
   414 
   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):
   415 class CWETypeViewsTab(EntityView):
   413     __regid__ = 'cwetype-views-tab'
   416     __regid__ = 'cwetype-views'
   414     __select__ = EntityView.__select__ & implements('CWEType')
   417     __select__ = EntityView.__select__ & implements('CWEType')
   415 
   418 
   416     def cell_call(self, row, col):
   419     def cell_call(self, row, col):
   417         entity = self.cw_rset.get_entity(row, col)
   420         entity = self.cw_rset.get_entity(row, col)
   418         etype = entity.name
   421         etype = entity.name
   425         rset = self._cw.etype_rset(etype)
   428         rset = self._cw.etype_rset(etype)
   426         return [v for v in self._cw.vreg['views'].possible_views(self._cw, rset)
   429         return [v for v in self._cw.vreg['views'].possible_views(self._cw, rset)
   427                 if v.category != 'startupview']
   430                 if v.category != 'startupview']
   428 
   431 
   429 
   432 
       
   433 class CWETypeOneLineView(baseviews.OneLineView):
       
   434     __select__ = implements('CWEType')
       
   435 
       
   436     def cell_call(self, row, col, **kwargs):
       
   437         entity = self.cw_rset.get_entity(row, col)
       
   438         if entity.final:
       
   439             self.w(u'<em class="finalentity">')
       
   440         super(CWETypeOneLineView, self).cell_call(row, col, **kwargs)
       
   441         if entity.final:
       
   442             self.w(u'</em>')
       
   443 
       
   444 
   430 # CWRType ######################################################################
   445 # CWRType ######################################################################
   431 
   446 
   432 class CWRTypeSchemaView(primary.PrimaryView):
   447 class CWRTypePrimaryView(tabs.TabbedPrimaryView):
   433     __select__ = implements('CWRType')
   448     __select__ = implements('CWRType')
   434     title = _('in memory relation schema')
   449     tabs = [_('cwrtype-description'), _('cwrtype-permissions')]
   435     main_related_section = False
   450     default_tab = 'cwrtype-description'
   436 
   451 
   437     def render_entity_attributes(self, entity):
   452 
   438         super(CWRTypeSchemaView, self).render_entity_attributes(entity)
   453 class CWRTypeDescriptionTab(tabs.PrimaryTab):
       
   454     __regid__ = 'cwrtype-description'
       
   455     __select__ = implements('CWRType')
       
   456 
       
   457     def render_entity_attributes(self, entity, siderelations=None):
       
   458         self.w(u'<div>%s</div>' % xml_escape(entity.description or u''))
   439         rschema = self._cw.vreg.schema.rschema(entity.name)
   459         rschema = self._cw.vreg.schema.rschema(entity.name)
   440         viewer = SchemaViewer(self._cw)
       
   441         layout = viewer.visit_relationschema(rschema, title=False)
       
   442         self.w(uilib.ureport_as_html(layout))
       
   443         if not rschema.final:
   460         if not rschema.final:
   444             msg = self._cw._('graphical schema for %s') % entity.name
   461             msg = self._cw._('graphical schema for %s') % entity.name
   445             self.w(tags.img(src=entity.absolute_url(vid='schemagraph'),
   462             self.w(tags.img(src=entity.absolute_url(vid='schemagraph'),
   446                             alt=msg))
   463                             alt=msg))
       
   464         rset = self._cw.execute('Any R,ST,OT,C,CC,R WHERE R is CWRelation, '
       
   465                                 'R relation_type RT, RT eid %(x)s, '
       
   466                                 'R from_type ST, R to_type OT, '
       
   467                                 'R cardinality C, R composite CC',
       
   468                                 {'x': entity.eid})
       
   469         self.wview('table', rset, 'null',
       
   470                    headers=(_(u'relation'), _(u'subject'), _(u'object'),
       
   471                             _(u'cardinality'), _(u'composite'),
       
   472                             _(u'constraints')),
       
   473                    cellvids={5: 'rdef-constraints-cell'})
       
   474 
       
   475 
       
   476 class CWRTypePermTab(SecurityViewMixIn, EntityView):
       
   477     __regid__ = 'cwrtype-permissions'
       
   478     __select__ = implements('CWRType') & authenticated_user()
       
   479 
       
   480     def cell_call(self, row, col):
       
   481         self._cw.add_css('cubicweb.acl.css')
       
   482         entity = self.cw_rset.get_entity(row, col)
       
   483         rschema = self._cw.vreg.schema.rschema(entity.name)
       
   484         self.grouped_permissions_table(rschema)
       
   485 
       
   486 
       
   487 # CWAttribute / CWRelation #####################################################
       
   488 
       
   489 class CWRDEFPrimaryView(tabs.TabbedPrimaryView):
       
   490     __select__ = implements('CWRelation', 'CWAttribute')
       
   491     tabs = [_('cwrdef-description'), _('cwrdef-permissions')]
       
   492     default_tab = 'cwrdef-description'
       
   493 
       
   494 class CWRDEFDescriptionTab(tabs.PrimaryTab):
       
   495     __regid__ = 'cwrdef-description'
       
   496     __select__ = implements('CWRelation', 'CWAttribute')
       
   497 
       
   498 class CWRDEFPermTab(SecurityViewMixIn, EntityView):
       
   499     __regid__ = 'cwrdef-permissions'
       
   500     __select__ = implements('CWRelation', 'CWAttribute') & authenticated_user()
       
   501 
       
   502     def cell_call(self, row, col):
       
   503         entity = self.cw_rset.get_entity(row, col)
       
   504         rschema = self._cw.vreg.schema.rschema(entity.rtype.name)
       
   505         rdef = rschema.rdefs[(entity.stype.name, entity.otype.name)]
       
   506         self.display_permission(rdef)
       
   507 
       
   508 
       
   509 class CWRDEFNameView(tableview.CellView):
       
   510     """display relation name and its translation only in a cell view, link to
       
   511     relation definition's primary view (for use in entity type relations table
       
   512     for instance)
       
   513     """
       
   514     __regid__ = 'rdef-name-cell'
       
   515     __select__ = implements('CWRelation', 'CWAttribute')
       
   516 
       
   517     def cell_content(self, entity):
       
   518         rtype = entity.relation_type[0].name
       
   519         # XXX use contect entity + pgettext
       
   520         return u'%s (%s)' % (rtype, self._cw._(rtype))
       
   521 
       
   522 class CWRDEFObjectNameView(tableview.CellView):
       
   523     """same as CWRDEFNameView but when the context is the object entity
       
   524     """
       
   525     __regid__ = 'rdef-object-name-cell'
       
   526     __select__ = implements('CWRelation', 'CWAttribute')
       
   527 
       
   528     def cell_content(self, entity):
       
   529         rtype = entity.relation_type[0].name
       
   530         # XXX use contect entity + pgettext
       
   531         return u'%s (%s)' % (rtype, self._cw.__(rtype + '_object'))
       
   532 
       
   533 class CWRDEFConstraintsCell(EntityView):
       
   534     __regid__ = 'rdef-constraints-cell'
       
   535     __select__ = implements('CWAttribute', 'CWRelation')
       
   536 
       
   537     def cell_call(self, row, col):
       
   538         entity = self.cw_rset.get_entity(row, col)
       
   539         rschema = self._cw.vreg.schema.rschema(entity.rtype.name)
       
   540         rdef = rschema.rdefs[(entity.stype.name, entity.otype.name)]
       
   541         constraints = [xml_escape(str(c)) for c in getattr(rdef, 'constraints')]
       
   542         self.w(u', '.join(constraints))
       
   543 
   447 
   544 
   448 # schema images ###############################################################
   545 # schema images ###############################################################
   449 
   546 
   450 class RestrictedSchemaVisitorMixIn(object):
   547 class RestrictedSchemaVisitorMixIn(object):
   451     def __init__(self, req, *args, **kwargs):
   548     def __init__(self, req, *args, **kwargs):
   507         rschema = self._cw.vreg.schema.rschema(entity.name)
   604         rschema = self._cw.vreg.schema.rschema(entity.name)
   508         visitor = OneHopRSchemaVisitor(self._cw, rschema)
   605         visitor = OneHopRSchemaVisitor(self._cw, rschema)
   509         s2d.schema2dot(outputfile=tmpfile, visitor=visitor)
   606         s2d.schema2dot(outputfile=tmpfile, visitor=visitor)
   510 
   607 
   511 
   608 
   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 
       
   531 # misc: facets, actions ########################################################
   609 # misc: facets, actions ########################################################
   532 
   610 
   533 class CWFinalFacet(facet.AttributeFacet):
   611 class CWFinalFacet(facet.AttributeFacet):
   534     __regid__ = 'cwfinal-facet'
   612     __regid__ = 'cwfinal-facet'
   535     __select__ = facet.AttributeFacet.__select__ & implements('CWEType', 'CWRType')
   613     __select__ = facet.AttributeFacet.__select__ & implements('CWEType', 'CWRType')