merge tls-sprint
authorAurelien Campeas <aurelien.campeas@logilab.fr>
Tue, 28 Apr 2009 20:08:46 +0200
branchtls-sprint
changeset 1529 7d1794175e40
parent 1528 864ae7c15ef5 (current diff)
parent 1522 47b2ffbee760 (diff)
child 1530 849fd3d64f11
merge
web/form.py
--- a/cwconfig.py	Tue Apr 28 20:08:16 2009 +0200
+++ b/cwconfig.py	Tue Apr 28 20:08:46 2009 +0200
@@ -189,6 +189,14 @@
           'help': 'web server root url',
           'group': 'main', 'inputlevel': 1,
           }),
+        ('use-request-subdomain',
+         {'type' : 'yn',
+          'default': None,
+          'help': ('if set, base-url subdomain is replaced by the request\'s '
+                   'host, to help managing sites with several subdomains in a '
+                   'single cubicweb instance'),
+          'group': 'main', 'inputlevel': 1,
+          }),
         ('mangle-emails',
          {'type' : 'yn',
           'default': False,
--- a/etwist/server.py	Tue Apr 28 20:08:16 2009 +0200
+++ b/etwist/server.py	Tue Apr 28 20:08:46 2009 +0200
@@ -10,6 +10,7 @@
 import select
 from time import mktime
 from datetime import date, timedelta
+from urlparse import urlsplit, urlunsplit
 
 from twisted.application import service, strports
 from twisted.internet import reactor, task, threads
@@ -167,6 +168,12 @@
         else:
             https = False
             baseurl = self.base_url
+        if self.config['use-request-subdomain']:
+            scheme, netloc, url, query, fragment = urlsplit(baseurl)
+            if '.' in netloc:
+                netloc = '.'.join(host.split('.')[:1] + netloc.split('.')[1:])
+            baseurl = urlunsplit((scheme, netloc, url, query, fragment))
+            self.warning('base_url is %s for this request', baseurl)
         req = CubicWebTwistedRequestAdapter(request, self.appli.vreg, https, baseurl)
         if req.authmode == 'http':
             # activate realm-based auth
--- a/schemaviewer.py	Tue Apr 28 20:08:16 2009 +0200
+++ b/schemaviewer.py	Tue Apr 28 20:08:46 2009 +0200
@@ -157,7 +157,7 @@
             return layout
         _ = self.req._
         if self.req.user.matching_groups('managers'):
-            layout.append(self.format_acls(eschema, ('read', 'add', 'delete', 'update')))
+            # layout.append(self.format_acls(eschema, ('read', 'add', 'delete', 'update')))
             # possible views for this entity type
             views = [_(view.title) for view in self.possible_views(etype)]
             layout.append(Section(children=(Table(cols=1, rheaders=1,
--- a/server/sources/rql2sql.py	Tue Apr 28 20:08:16 2009 +0200
+++ b/server/sources/rql2sql.py	Tue Apr 28 20:08:46 2009 +0200
@@ -951,6 +951,8 @@
             if rel is not None:
                 rel._q_needcast = value
             return self.keyword_map[value]()
+        if constant.type == 'Boolean':
+            value = self.dbms_helper.boolean_value(value)
         if constant.type == 'Substitute':
             _id = constant.value
             if isinstance(_id, unicode):
--- a/web/data/cubicweb.acl.css	Tue Apr 28 20:08:16 2009 +0200
+++ b/web/data/cubicweb.acl.css	Tue Apr 28 20:08:46 2009 +0200
@@ -9,19 +9,90 @@
 /* security edition form (views/management.py)                                */
 /******************************************************************************/
 
+h2.schema{
+ background : #ff7700;
+ color: #fff;
+ font-weight: bold;
+ padding : 0.1em 0.3em;
+}
+
+
+h3.schema{ 
+ font-weight: bold;
+}
+
+h4 a,
+h4 a:link,
+h4 a:visited{ 
+ color:#000;
+ }
+
 table.schemaInfo {
-  margin: 1ex 1em;
+  margin: 1em 0em;
   text-align: left;
   border: 1px solid black;
   border-collapse: collapse;
+  width:100%;
 }
 
 table.schemaInfo th,
 table.schemaInfo td {
-  padding: 0em 1em;
-  border: 1px solid black;
+  padding: .3em .5em;
+  border: 1px solid grey;
+  width:33%; 
+}
+
+
+table.schemaInfo tr th {   
+ padding: 0.2em 0px 0.2em 5px;
+ background-image:none;
+ background-color:#dfdfdf;
+}
+
+table.schemaInfo thead tr {
+  border: 1px solid #dfdfdf;
+} 
+
+table.schemaInfo td {
+  padding: 3px 10px 3px 5px; 
+
 }
 
+.users{ 
+ color : #00CC33;
+ font-weight: bold }
+
+.guests{ 
+ color :  #ff7700;
+ font-weight: bold;
+}
+
+.staff{  
+ color : #0083ab;
+ font-weight: bold;
+}
+
+.owners{ 
+ color : #8b0000;
+ font-weight: bold;
+}
+
+.discret,
+a.grey{ 
+ color:#666;
+}
+
+a.grey:hover{ 
+ color:#000;
+}
+
+.red{ 
+ color :  #ff7700;
+ }
+
+div#schema_security{ 
+ width:780px;
+ }
 /******************************************************************************/
 /* user groups edition form (views/euser.py)                                  */
 /******************************************************************************/
--- a/web/data/external_resources	Tue Apr 28 20:08:16 2009 +0200
+++ b/web/data/external_resources	Tue Apr 28 20:08:46 2009 +0200
@@ -52,3 +52,4 @@
 DOWNLOAD_ICON = DATADIR/download.gif
 UPLOAD_ICON = DATADIR/upload.gif
 GMARKER_ICON = DATADIR/gmap_blue_marker.png
+UP_ICON = DATADIR/up.gif
Binary file web/data/up.gif has changed
--- a/web/form.py	Tue Apr 28 20:08:16 2009 +0200
+++ b/web/form.py	Tue Apr 28 20:08:46 2009 +0200
@@ -30,28 +30,6 @@
     http_cache_manager = NoHTTPCacheManager
     add_to_breadcrumbs = False
 
-    def __init__(self, req, rset, **kwargs):
-        super(FormViewMixIn, self).__init__(req, rset, **kwargs)
-        # get validation session data which may have been previously set.
-        # deleting validation errors here breaks form reloading (errors are
-        # no more available), they have to be deleted by application's publish
-        # method on successful commit
-        formurl = req.url()
-        forminfo = req.get_session_data(formurl)
-        if forminfo:
-            req.data['formvalues'] = forminfo['values']
-            req.data['formerrors'] = errex = forminfo['errors']
-            req.data['displayederrors'] = set()
-            # if some validation error occured on entity creation, we have to
-            # get the original variable name from its attributed eid
-            foreid = errex.entity
-            for var, eid in forminfo['eidmap'].items():
-                if foreid == eid:
-                    errex.eid = var
-                    break
-            else:
-                errex.eid = foreid
-
     def html_headers(self):
         """return a list of html headers (eg something to be inserted between
         <head> and </head> of the returned page
@@ -80,22 +58,13 @@
             self.req.set_page_data('rql_varmaker', varmaker)
         self.varmaker = varmaker
 
-    # XXX deprecated with new form system. Should disappear
-
-    domid = 'entityForm'
-    category = 'form'
-    controller = 'edit'
-    http_cache_manager = NoHTTPCacheManager
-    add_to_breadcrumbs = False
-
     def __init__(self, req, rset, **kwargs):
         super(FormMixIn, self).__init__(req, rset, **kwargs)
         # get validation session data which may have been previously set.
         # deleting validation errors here breaks form reloading (errors are
         # no more available), they have to be deleted by application's publish
         # method on successful commit
-        formurl = req.url()
-        forminfo = req.get_session_data(formurl)
+        forminfo = req.get_session_data(req.url())
         if forminfo:
             req.data['formvalues'] = forminfo['values']
             req.data['formerrors'] = errex = forminfo['errors']
@@ -110,6 +79,14 @@
             else:
                 errex.eid = foreid
 
+    # XXX deprecated with new form system. Should disappear
+
+    domid = 'entityForm'
+    category = 'form'
+    controller = 'edit'
+    http_cache_manager = NoHTTPCacheManager
+    add_to_breadcrumbs = False
+
     def html_headers(self):
         """return a list of html headers (eg something to be inserted between
         <head> and </head> of the returned page
@@ -371,10 +348,11 @@
         values found in 1. and 2. are expected te be already some 'display'
         value while those found in 3. and 4. are expected to be correctly typed.
         """
-        if field.name in self._previous_values:
-            value = self._previous_values[field.name]
-        elif field.name in self.req.form:
-            value = self.req.form[field.name]
+        qname = self.form_field_name(field)
+        if qname in self._previous_values:
+            value = self._previous_values[qname]
+        elif qname in self.req.form:
+            value = self.req.form[qname]
         else:
             if field.name in rendervalues:
                 value = rendervalues[field.name]
@@ -449,6 +427,9 @@
                 self.form_add_hidden('__linkto', linkto)
                 msg = '%s %s' % (msg, self.req._('and linked'))
             self.form_add_hidden('__message', msg)
+        # in case of direct instanciation
+        self.schema = self.edited_entity.schema
+        self.vreg = self.edited_entity.vreg
 
     def _errex_match_field(self, errex, field):
         """return true if the field has some error in given validation exception
--- a/web/views/cwuser.py	Tue Apr 28 20:08:16 2009 +0200
+++ b/web/views/cwuser.py	Tue Apr 28 20:08:46 2009 +0200
@@ -57,10 +57,11 @@
         return entity.name()
 
     def is_side_related(self, rschema, eschema):
+        # XXX only bookmarked_by defined in cw...
         return  rschema.type in ['interested_in', 'tags',
-                                 'todo_by', 'bookmarked_by',
+                                 'todo_by', 'bookmarked_by']
 
-                                 ]
+
 class FoafView(EntityView):
     id = 'foaf'
     __select__ = implements('CWUser')
--- a/web/views/management.py	Tue Apr 28 20:08:16 2009 +0200
+++ b/web/views/management.py	Tue Apr 28 20:08:46 2009 +0200
@@ -22,8 +22,47 @@
 SUBMIT_MSGID = _('Submit bug report')
 MAIL_SUBMIT_MSGID = _('Submit bug report by mail')
 
+class SecurityViewMixIn(object):
+    """display security information for a given schema """
+    def schema_definition(self, eschema, link=True,  access_types=None):
+        w = self.w
+        _ = self.req._
+        if not access_types:
+            access_types = eschema.ACTIONS
+        w(u'<table class="schemaInfo">')
+        w(u'<tr><th>%s</th><th>%s</th><th>%s</th></tr>' % ( 
+            _("permission"), _('granted to groups'), _('rql expressions')))
+        for access_type in access_types:
+            w(u'<tr>')
+            w(u'<td>%s</td>' % _('%s_perm' % access_type))
+            groups = eschema.get_groups(access_type)
+            l = []
+            groups = [(_(group), group) for group in groups]
+            for trad, group in sorted(groups):
+                if link:
+                    l.append(u'<a href="%s" class="%s">%s</a><br/>' % (
+                    self.build_url('egroup/%s' % group), group, trad))
+                else:
+                    l.append(u'<div class="%s">%s</div>' % (group, trad))
+            w(u'<td>%s</td>' % u''.join(l))
+            rqlexprs = eschema.get_rqlexprs(access_type)
+            w(u'<td>%s</td>' % u'<br/><br/>'.join(expr.expression for expr in rqlexprs))
+            w(u'</tr>\n')
+        w(u'</table>')
 
-class SecurityManagementView(EntityView):
+    def has_schema_modified_permissions(self, eschema, access_types):
+        """ return True if eschema's actual permissions are diffrents
+        from the default ones
+        """
+        for access_type in access_types:
+            if eschema.get_rqlexprs(access_type):
+                return True
+            if eschema.get_groups(access_type) != \
+                    frozenset(eschema.get_default_groups()[access_type]):
+                return True
+        return False
+
+class SecurityManagementView(EntityView, SecurityViewMixIn):
     """display security information for a given entity"""
     id = 'security'
     title = _('security')
@@ -40,7 +79,7 @@
              html_escape(entity.dc_title())))
         # first show permissions defined by the schema
         self.w('<h2>%s</h2>' % _('schema\'s permissions definitions'))
-        self.schema_definition(entity)
+        self.schema_definition(entity.e_schema)
         self.w('<h2>%s</h2>' % _('manage security'))
         # ownership information
         if self.schema.rschema('owned_by').has_perm(self.req, 'add',
@@ -48,7 +87,7 @@
             self.owned_by_edit_form(entity)
         else:
             self.owned_by_information(entity)
-        # epermissions
+        # cwpermissions
         if 'require_permission' in entity.e_schema.subject_relations():
             w('<h3>%s</h3>' % _('permissions for this entity'))
             reqpermschema = self.schema.rschema('require_permission')
@@ -56,26 +95,6 @@
             if reqpermschema.has_perm(self.req, 'add', fromeid=entity.eid):
                 self.require_permission_edit_form(entity)
 
-    def schema_definition(self, entity):
-        w = self.w
-        _ = self.req._
-        w(u'<table class="schemaInfo">')
-        w(u'<tr><th>%s</th><th>%s</th><th>%s</th></tr>' % (
-            _("access type"), _('granted to groups'), _('rql expressions')))
-        for access_type in ('read', 'add', 'update', 'delete'):
-            w(u'<tr>')
-            w(u'<th>%s</th>' % self.req.__('%s_permission' % access_type))
-            groups = entity.e_schema.get_groups(access_type)
-            l = []
-            for group in groups:
-                l.append(u'<a href="%s">%s</a>' % (
-                    self.build_url('egroup/%s' % group), _(group)))
-            w(u'<td>%s</td>' % u', '.join(l))
-            rqlexprs = entity.e_schema.get_rqlexprs(access_type)
-            w(u'<td>%s</td>' % u'<br/>'.join(expr.expression for expr in rqlexprs))
-            w(u'</tr>\n')
-        w(u'</table>')
-
     def owned_by_edit_form(self, entity):
         self.w('<h3>%s</h3>' % self.req._('ownership'))
         msg = self.req._('ownerships have been changed')
@@ -116,18 +135,18 @@
             w(u'<table class="schemaInfo">')
             w(u'<tr><th>%s</th><th>%s</th></tr>' % (_("permission"),
                                                     _('granted to groups')))
-            for eperm in entity.require_permission:
+            for cwperm in entity.require_permission:
                 w(u'<tr>')
                 if dellinktempl:
-                    w(u'<td>%s%s</td>' % (dellinktempl % eperm.eid,
-                                          eperm.view('oneline')))
+                    w(u'<td>%s%s</td>' % (dellinktempl % cwperm.eid,
+                                          cwperm.view('oneline')))
                 else:
-                    w(u'<td>%s</td>' % eperm.view('oneline'))
-                w(u'<td>%s</td>' % self.view('csv', eperm.related('require_group'), 'null'))
+                    w(u'<td>%s</td>' % cwperm.view('oneline'))
+                w(u'<td>%s</td>' % self.view('csv', cwperm.related('require_group'), 'null'))
                 w(u'</tr>\n')
             w(u'</table>')
         else:
-            self.w(self.req._('no associated epermissions'))
+            self.w(self.req._('no associated permissions'))
 
     def require_permission_edit_form(self, entity):
         w = self.w
--- a/web/views/primary.py	Tue Apr 28 20:08:16 2009 +0200
+++ b/web/views/primary.py	Tue Apr 28 20:08:46 2009 +0200
@@ -44,27 +44,41 @@
 
     def render_entity(self, entity):
         """return html to display the given entity"""
-        siderelations = []
         self.render_entity_title(entity)
         self.render_entity_metadata(entity)
         # entity's attributes and relations, excluding meta data
         # if the entity isn't meta itself
-        boxes = self._preinit_side_related(entity, siderelations)
+        boxes = self._preinit_side_related(entity)
         if boxes:
             self.w(u'<table width="100%"><tr><td width="75%">')
         self.w(u'<div>')
         self.w(u'<div class="mainInfo">')
-        self.render_entity_attributes(entity, siderelations)
+        try:
+            self.render_entity_attributes(entity)
+        except TypeError: # XXX bw compat
+            warn('siderelations argument of render_entity_attributes is '
+                 'deprecated')
+            self.render_entity_attributes(entity, [])
         self.w(u'</div>')
         self.content_navigation_components('navcontenttop')
         if self.main_related_section:
-            self.render_entity_relations(entity, siderelations)
+            try:
+                self.render_entity_relations(entity)
+            except TypeError: # XXX bw compat
+                warn('siderelations argument of render_entity_relations is '
+                     'deprecated')
+                self.render_entity_relations(entity, [])
         self.w(u'</div>')
         if boxes:
             self.w(u'</td><td>')
             # side boxes
             self.w(u'<div class="primaryRight">')
-            self.render_side_related(entity, siderelations)
+            try:
+                self.render_side_related(entity)
+            except TypeError: # XXX bw compat
+                warn('siderelations argument of render_entity_relations is '
+                     'deprecated')
+                self.render_entity_relations(entity, [])
             self.w(u'</div>')
             self.w(u'</td></tr></table>')
         self.content_navigation_components('navcontentbottom')
@@ -117,7 +131,7 @@
         """default implementation return an empty string"""
         return u''
 
-    def render_entity_attributes(self, entity, siderelations):
+    def render_entity_attributes(self, entity, siderelations=None):
         for rschema, targetschema in self.iter_attributes(entity):
             attr = rschema.type
             if targetschema.type in ('Password', 'Bytes'):
@@ -134,56 +148,51 @@
                 continue
             self._render_related_entities(entity, rschema, value)
 
-    def _preinit_side_related(self, entity, siderelations):
-        self._sideboxes = None
-        self._related_entities = []
+    def _preinit_side_related(self, entity):
+        self._sideboxes = []
         if hasattr(self, 'get_side_boxes_defs'):
-            self._sideboxes = [(label, rset) for label, rset in self.get_side_boxes_defs(entity)
+            self._sideboxes = [(label, rset, 'sidebox') for label, rset in self.get_side_boxes_defs(entity)
                                if rset]
         else:
             eschema = entity.e_schema
             maxrelated = self.req.property_value('navigation.related-limit')
-            for rschema, targetschemas, x in self.iter_relations(entity):
+            for rschema, targetschemas, role in self.iter_relations(entity):
+                if self.is_side_related(rschema, eschema):
+                    try:
+                        related = entity.related(rschema.type, role, limit=maxrelated+1)
+                    except Unauthorized:
+                        continue
+                    if not related:
+                        continue
+                    label = display_name(self.req, rschema.type, role)
+                    self._sideboxes.append((label, related, 'autolimited'))
+        self._contextboxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset,
+                                                                  row=self.row, view=self,
+                                                                  context='incontext'))
+        return self._sideboxes or self._contextboxes
+
+    def render_entity_relations(self, entity, siderelations=None):
+        eschema = entity.e_schema
+        for rschema, targetschemas, x in self.iter_relations(entity):
+            if not self.is_side_related(rschema, eschema):
                 try:
                     related = entity.related(rschema.type, x, limit=maxrelated+1)
                 except Unauthorized:
                     continue
-                if not related:
-                    continue
-                if self.is_side_related(rschema, eschema):
-                    siderelations.append((rschema, related, x))
-                    continue
-                self._related_entities.append((rschema, related, x))
-        self._boxes_in_context = list(self.vreg.possible_vobjects('boxes', self.req, self.rset,
-                                                 row=self.row, view=self,
-                                                 context='incontext'))
-        return self._sideboxes or self._boxes_in_context or self._related_entities or siderelations
-
-    def render_entity_relations(self, entity, siderelations):
-        if self._related_entities:
-            for rschema, related, x in self._related_entities:
                 self._render_related_entities(entity, rschema, related, x)
 
 
-    def render_side_related(self, entity, siderelations):
+    def render_side_related(self, entity, siderelations=None):
         """display side related relations:
         non-meta in a first step, meta in a second step
         """
         if self._sideboxes:
-            for label, rset in self._sideboxes:
+            for label, rset, vid in self._sideboxes:
                 self.w(u'<div class="sideRelated">')
-                self.wview('sidebox', rset, title=label)
+                self.wview(vid, rset, title=label)
                 self.w(u'</div>')
-        elif siderelations:
-            self.w(u'<div class="sideRelated">')
-            for relatedinfos in siderelations:
-                # if not relatedinfos[0].meta:
-                #    continue
-                self._render_related_entities(entity, *relatedinfos)
-            self.w(u'</div>')
-
-        if self._boxes_in_context:
-            for box in self._boxes_in_context:
+        if self._contextboxes:
+            for box in self._contextboxes:
                 try:
                     box.dispatch(w=self.w, row=self.row)
                 except NotImplementedError:
@@ -203,24 +212,31 @@
         else:
             if not related:
                 return
-            show_label = self.show_rel_label
-            # if not too many entities, show them all in a list
-            maxrelated = self.req.property_value('navigation.related-limit')
-            if related.rowcount <= maxrelated:
-                if related.rowcount == 1:
-                    value = self.view('incontext', related, row=0)
-                elif 1 < related.rowcount <= 5:
-                    value = self.view('csv', related)
-                else:
-                    value = '<div>' + self.view('simplelist', related) + '</div>'
-            # else show links to display related entities
-            else:
-                rql = related.printable_rql()
-                related.limit(maxrelated)
-                value = '<div>' + self.view('simplelist', related)
-                value += '[<a href="%s">%s</a>]' % (self.build_url(rql=rql),
-                                                    self.req._('see them all'))
-                value +=  '</div>'
+            value = self.view('autolimited', related)
         label = display_name(self.req, rschema.type, role)
         self.field(label, value, show_label=show_label, tr=False)
 
+
+class RelatedView(EntityView):
+    id = 'autolimited'
+    def call(self):
+        # if not too many entities, show them all in a list
+        maxrelated = self.req.property_value('navigation.related-limit')
+        if self.rset.rowcount <= maxrelated:
+            if self.rset.rowcount == 1:
+                self.wview('incontext', self.rset, row=0)
+            elif 1 < self.rset.rowcount <= 5:
+                self.wview('csv', self.rset)
+            else:
+                self.w(u'<div>')
+                self.wview('simplelist', self.rset)
+                self.w(u'</div>')
+        # else show links to display related entities
+        else:
+            rql = self.rset.printable_rql()
+            self.rset.limit(maxself.rset)
+            self.w(u'<div>')
+            self.wview('simplelist', self.rset)
+            self.w(u'[<a href="%s">%s</a>]' % (self.build_url(rql=rql),
+                                               self.req._('see them all')))
+            self.w(u'</div>')
--- a/web/views/startup.py	Tue Apr 28 20:08:16 2009 +0200
+++ b/web/views/startup.py	Tue Apr 28 20:08:46 2009 +0200
@@ -10,10 +10,12 @@
 from logilab.common.textutils import unormalize
 from logilab.mtconverter import html_escape
 
-from cubicweb.view import StartupView
-from cubicweb.common.uilib import ureport_as_html, ajax_replace_url
+from cubicweb.common.uilib import ureport_as_html, unormalize, ajax_replace_url
+from cubicweb.common.view import StartupView
+from cubicweb.common.selectors import match_user_group
 from cubicweb.web.httpcache import EtagHTTPCacheManager
-
+from cubicweb.web.views.management import SecurityViewMixIn
+from copy import deepcopy
 _ = unicode
 
 
@@ -159,25 +161,135 @@
     def call(self):
         """display schema information"""
         self.req.add_js('cubicweb.ajax.js')
-        self.req.add_css('cubicweb.schema.css')
+        self.req.add_css(('cubicweb.schema.css','cubicweb.acl.css'))
         withmeta = int(self.req.form.get('withmeta', 0))
+        section = self.req.form.get('sec', '')
         self.w(u'<img src="%s" alt="%s"/>\n' % (
             html_escape(self.req.build_url('view', vid='schemagraph', withmeta=withmeta)),
             self.req._("graphical representation of the application'schema")))
         if withmeta:
             self.w(u'<div><a href="%s">%s</a></div>' % (
-                self.build_url('schema', withmeta=0),
+                html_escape(self.build_url('schema', withmeta=0, sec=section)),
                 self.req._('hide meta-data')))
         else:
             self.w(u'<div><a href="%s">%s</a></div>' % (
-                self.build_url('schema', withmeta=1),
+                html_escape(self.build_url('schema', withmeta=1, sec=section)),
                 self.req._('show meta-data')))
-        self.w(u'<div id="detailed_schema"><a href="%s">%s</a></div>' %
+        self.w(u'<a href="%s">%s</a><br/>' %
                (html_escape(ajax_replace_url('detailed_schema', '', 'schematext',
                                              skipmeta=int(not withmeta))),
                 self.req._('detailed schema view')))
+        if self.req.user.matching_groups('managers'):
+            self.w(u'<a href="%s">%s</a>' %
+                   (html_escape(ajax_replace_url('detailed_schema', '', 'schema_security',
+                                                 skipmeta=int(not withmeta))),
+                self.req._('security')))
+        self.w(u'<div id="detailed_schema">')
+        if section:
+            self.wview(section, None)
+        self.w(u'</div>')
+
+    
+class ManagerSchemaPermissionsView(StartupView, SecurityViewMixIn):
+    id = 'schema_security'
+    require_groups = ('managers',)
+    __selectors__ = StartupView.__selectors__ + (match_user_group,)
+
+    def call(self, display_relations=True,
+             skiprels=('is', 'is_instance_of', 'identity', 'owned_by', 'created_by')):
+        _ = self.req._
+        formparams = {}
+        formparams['sec'] = self.id
+        formparams['withmeta'] = int(self.req.form.get('withmeta', True))
+        schema = self.schema
+        # compute entities
+        entities = [eschema for eschema in schema.entities()
+                   if not eschema.is_final()]
+        if not formparams['withmeta']:
+            entities = [eschema for eschema in entities
+                        if not eschema.meta]
+        # compute relations
+        relations = []    
+        if display_relations:
+            relations = [rschema for rschema in schema.relations()
+                         if not (rschema.is_final() or rschema.type in skiprels)]
+            if not formparams['withmeta']:
+                relations = [rschema for rschema in relations
+                             if not rschema.meta]
+        # index
+        self.w(u'<div id="schema_security"><a id="index" href="index"/>')
+        self.w(u'<h2 class="schema">%s</h2>' % _('index').capitalize())
+        self.w(u'<h4>%s</h4>' %   _('Entities').capitalize())
+        ents = []
+        for eschema in sorted(entities):
+            url = html_escape(self.build_url('schema', **formparams) + '#' + eschema.type)
+            ents.append(u'<a class="grey" href="%s">%s</a> (%s)' % (url,  eschema.type, _(eschema.type)))
+        self.w('%s' %  ', '.join(ents))
+        self.w(u'<h4>%s</h4>' % (_('relations').capitalize()))
+        rels = []
+        for eschema in sorted(relations):
+            url = html_escape(self.build_url('schema', **formparams) + '#' + eschema.type)
+            rels.append(u'<a class="grey" href="%s">%s</a> (%s), ' %  (url , eschema.type, _(eschema.type)))
+        self.w('%s' %  ', '.join(ents))
+        # entities
+        self.display_entities(entities, formparams)
+        # relations
+        if relations:
+            self.display_relations(relations, formparams)
+        self.w(u'</div>')
+
+    def display_entities(self, entities, formparams):
+        _ = self.req._
+        self.w(u'<a id="entities" href="entities"/>')
+        self.w(u'<h2 class="schema">%s</h2>' % _('permissions for entities').capitalize())
+        for eschema in sorted(entities):
+            self.w(u'<a id="%s" href="%s"/>' %  (eschema.type, eschema.type))
+            self.w(u'<h3 class="schema">%s (%s) ' % (eschema.type, _(eschema.type)))
+            url = html_escape(self.build_url('schema', **formparams) + '#index')
+            self.w(u'<a href="%s"><img src="%s" alt="%s"/></a>' % (url,  self.req.external_resource('UP_ICON'), _('up')))
+            self.w(u'</h3>')
+            self.w(u'<div style="margin: 0px 1.5em">')
+            self.schema_definition(eschema, link=False)
+
+            # display entity attributes only if they have some permissions modified
+            modified_attrs = []
+            for attr, etype in  eschema.attribute_definitions():
+                if self.has_schema_modified_permissions(attr, attr.ACTIONS):
+                    modified_attrs.append(attr)
+            if  modified_attrs:
+                self.w(u'<h4>%s</h4>' % _('attributes with modified permissions:').capitalize())
+                self.w(u'</div>')
+                self.w(u'<div style="margin: 0px 6em">')
+                for attr in  modified_attrs:
+                    self.w(u'<h4 class="schema">%s (%s)</h4> ' % (attr.type, _(attr.type)))
+                    self.schema_definition(attr, link=False)
+                self.w(u'</div>')
+            else:
+                self.w(u'</div>')
 
 
+    def display_relations(self, relations, formparams):
+        _ = self.req._
+        self.w(u'<a id="relations" href="relations"/>')
+        self.w(u'<h2 class="schema">%s </h2>' % _('permissions for relations').capitalize())
+        for rschema in sorted(relations):
+            self.w(u'<a id="%s" href="%s"/>' %  (rschema.type, rschema.type))
+            self.w(u'<h3 class="schema">%s (%s) ' % (rschema.type, _(rschema.type)))
+            url = html_escape(self.build_url('schema', **formparams) + '#index')
+            self.w(u'<a href="%s"><img src="%s" alt="%s"/></a>' % (url,  self.req.external_resource('UP_ICON'), _('up')))
+            self.w(u'</h3>')
+            self.w(u'<div style="margin: 0px 1.5em">')
+            subjects = [str(subj) for subj in rschema.subjects()]
+            self.w(u'<div><strong>%s</strong> %s (%s)</div>' % (_('subject_plural:'),
+                                                ', '.join( [str(subj) for subj in rschema.subjects()]),
+                                                ', '.join( [_(str(subj)) for subj in rschema.subjects()])))
+            self.w(u'<div><strong>%s</strong> %s (%s)</div>' % (_('object_plural:'),
+                                                ', '.join( [str(obj) for obj in rschema.objects()]),
+                                                ', '.join( [_(str(obj)) for obj in rschema.objects()])))
+            self.schema_definition(rschema, link=False)
+            self.w(u'</div>')
+
+                
 class SchemaUreportsView(StartupView):
     id = 'schematext'