# HG changeset patch # User Aurelien Campeas # Date 1240942126 -7200 # Node ID 7d1794175e403e74f5c9f3f472a8bd7433033526 # Parent 864ae7c15ef5f7be6c973db1b6e1a9276e38a4b6# Parent 47b2ffbee760a64b7fc6e1d7f5c2bf7dbd44d396 merge diff -r 864ae7c15ef5 -r 7d1794175e40 cwconfig.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, diff -r 864ae7c15ef5 -r 7d1794175e40 etwist/server.py --- 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 diff -r 864ae7c15ef5 -r 7d1794175e40 i18n/en.po diff -r 864ae7c15ef5 -r 7d1794175e40 i18n/es.po diff -r 864ae7c15ef5 -r 7d1794175e40 i18n/fr.po diff -r 864ae7c15ef5 -r 7d1794175e40 schemaviewer.py --- 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, diff -r 864ae7c15ef5 -r 7d1794175e40 server/sources/rql2sql.py --- 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): diff -r 864ae7c15ef5 -r 7d1794175e40 web/data/cubicweb.acl.css --- 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) */ /******************************************************************************/ diff -r 864ae7c15ef5 -r 7d1794175e40 web/data/external_resources --- 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 diff -r 864ae7c15ef5 -r 7d1794175e40 web/data/up.gif Binary file web/data/up.gif has changed diff -r 864ae7c15ef5 -r 7d1794175e40 web/form.py --- 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 and 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 and 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 diff -r 864ae7c15ef5 -r 7d1794175e40 web/views/cwuser.py --- 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') diff -r 864ae7c15ef5 -r 7d1794175e40 web/views/management.py --- 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'') + w(u'' % ( + _("permission"), _('granted to groups'), _('rql expressions'))) + for access_type in access_types: + w(u'') + w(u'' % _('%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'%s
' % ( + self.build_url('egroup/%s' % group), group, trad)) + else: + l.append(u'
%s
' % (group, trad)) + w(u'' % u''.join(l)) + rqlexprs = eschema.get_rqlexprs(access_type) + w(u'' % u'

'.join(expr.expression for expr in rqlexprs)) + w(u'\n') + w(u'
%s%s%s
%s%s%s
') -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('

%s

' % _('schema\'s permissions definitions')) - self.schema_definition(entity) + self.schema_definition(entity.e_schema) self.w('

%s

' % _('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('

%s

' % _('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'') - w(u'' % ( - _("access type"), _('granted to groups'), _('rql expressions'))) - for access_type in ('read', 'add', 'update', 'delete'): - w(u'') - w(u'' % self.req.__('%s_permission' % access_type)) - groups = entity.e_schema.get_groups(access_type) - l = [] - for group in groups: - l.append(u'%s' % ( - self.build_url('egroup/%s' % group), _(group))) - w(u'' % u', '.join(l)) - rqlexprs = entity.e_schema.get_rqlexprs(access_type) - w(u'' % u'
'.join(expr.expression for expr in rqlexprs)) - w(u'\n') - w(u'
%s%s%s
%s%s%s
') - def owned_by_edit_form(self, entity): self.w('

%s

' % self.req._('ownership')) msg = self.req._('ownerships have been changed') @@ -116,18 +135,18 @@ w(u'') w(u'' % (_("permission"), _('granted to groups'))) - for eperm in entity.require_permission: + for cwperm in entity.require_permission: w(u'') if dellinktempl: - w(u'' % (dellinktempl % eperm.eid, - eperm.view('oneline'))) + w(u'' % (dellinktempl % cwperm.eid, + cwperm.view('oneline'))) else: - w(u'' % eperm.view('oneline')) - w(u'' % self.view('csv', eperm.related('require_group'), 'null')) + w(u'' % cwperm.view('oneline')) + w(u'' % self.view('csv', cwperm.related('require_group'), 'null')) w(u'\n') w(u'
%s%s
%s%s%s%s%s%s%s%s
') 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 diff -r 864ae7c15ef5 -r 7d1794175e40 web/views/primary.py --- 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'
') self.w(u'
') self.w(u'
') - 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'
') 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'
') if boxes: self.w(u'
') # side boxes self.w(u'
') - 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'
') self.w(u'
') 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'
') - self.wview('sidebox', rset, title=label) + self.wview(vid, rset, title=label) self.w(u'
') - elif siderelations: - self.w(u'
') - for relatedinfos in siderelations: - # if not relatedinfos[0].meta: - # continue - self._render_related_entities(entity, *relatedinfos) - self.w(u'
') - - 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 = '
' + self.view('simplelist', related) + '
' - # else show links to display related entities - else: - rql = related.printable_rql() - related.limit(maxrelated) - value = '
' + self.view('simplelist', related) - value += '[%s]' % (self.build_url(rql=rql), - self.req._('see them all')) - value += '
' + 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'
') + self.wview('simplelist', self.rset) + self.w(u'
') + # else show links to display related entities + else: + rql = self.rset.printable_rql() + self.rset.limit(maxself.rset) + self.w(u'
') + self.wview('simplelist', self.rset) + self.w(u'[%s]' % (self.build_url(rql=rql), + self.req._('see them all'))) + self.w(u'
') diff -r 864ae7c15ef5 -r 7d1794175e40 web/views/startup.py --- 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'%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'
%s
' % ( - 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'
%s
' % ( - self.build_url('schema', withmeta=1), + html_escape(self.build_url('schema', withmeta=1, sec=section)), self.req._('show meta-data'))) - self.w(u'
%s
' % + self.w(u'%s
' % (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'%s' % + (html_escape(ajax_replace_url('detailed_schema', '', 'schema_security', + skipmeta=int(not withmeta))), + self.req._('security'))) + self.w(u'
') + if section: + self.wview(section, None) + self.w(u'
') + + +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'
') + self.w(u'

%s

' % _('index').capitalize()) + self.w(u'

%s

' % _('Entities').capitalize()) + ents = [] + for eschema in sorted(entities): + url = html_escape(self.build_url('schema', **formparams) + '#' + eschema.type) + ents.append(u'
%s (%s)' % (url, eschema.type, _(eschema.type))) + self.w('%s' % ', '.join(ents)) + self.w(u'

%s

' % (_('relations').capitalize())) + rels = [] + for eschema in sorted(relations): + url = html_escape(self.build_url('schema', **formparams) + '#' + eschema.type) + rels.append(u'%s (%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'
') + + def display_entities(self, entities, formparams): + _ = self.req._ + self.w(u'') + self.w(u'

%s

' % _('permissions for entities').capitalize()) + for eschema in sorted(entities): + self.w(u'
' % (eschema.type, eschema.type)) + self.w(u'

%s (%s) ' % (eschema.type, _(eschema.type))) + url = html_escape(self.build_url('schema', **formparams) + '#index') + self.w(u'%s' % (url, self.req.external_resource('UP_ICON'), _('up'))) + self.w(u'

') + self.w(u'
') + 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'

%s

' % _('attributes with modified permissions:').capitalize()) + self.w(u'
') + self.w(u'
') + for attr in modified_attrs: + self.w(u'

%s (%s)

' % (attr.type, _(attr.type))) + self.schema_definition(attr, link=False) + self.w(u'
') + else: + self.w(u'') + def display_relations(self, relations, formparams): + _ = self.req._ + self.w(u'') + self.w(u'

%s

' % _('permissions for relations').capitalize()) + for rschema in sorted(relations): + self.w(u'
' % (rschema.type, rschema.type)) + self.w(u'

%s (%s) ' % (rschema.type, _(rschema.type))) + url = html_escape(self.build_url('schema', **formparams) + '#index') + self.w(u'%s' % (url, self.req.external_resource('UP_ICON'), _('up'))) + self.w(u'

') + self.w(u'
') + subjects = [str(subj) for subj in rschema.subjects()] + self.w(u'
%s %s (%s)
' % (_('subject_plural:'), + ', '.join( [str(subj) for subj in rschema.subjects()]), + ', '.join( [_(str(subj)) for subj in rschema.subjects()]))) + self.w(u'
%s %s (%s)
' % (_('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'
') + + class SchemaUreportsView(StartupView): id = 'schematext'