Fix (de)serialization of ComputedRelation read permissions
For normal relation types, permissions don't need to be stored since
they're just default values for the relation definitions. However,
computed relations are serialized (as CWComputedRType), while their
relation definitions are added at schema finalization time, and are only
in memory. So add the 'read_permission' relation to CWComputedRType,
and the appropriate hooks to save and restore those permissions.
To avoid having to touch yams, we drop the 'add' and 'delete'
permissions from the default computed relation permissions; this should
probably be backported there. The actual permissions (set on the
relation definitions) are hardcoded in finalize_computed_relations
anyway.
In deserialize_schema, the CWComputedRType handling needs to be delayed
a little bit, until after we've called deserialize_ertype_permissions.
The rql2sql test is adjusted because CWComputedRType has a 'name'
attribute and the 'read_permission' relation, which generates ambiguity
vs CWEType.
We add an explicit CubicWebRelationSchema.check_permission_definitions,
since we need to check both that computed and non-computed rtypes are
defined properly.
Based on report and initial patch from Christophe de Vienne (thanks!).
Closes #5706307
# Author: David Goodger
# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
#
# CubicWeb is free software: you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option)
# any later version.
#
# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""
"""
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2 $
# Date: $Date: 2005-07-04 16:36:50 $
# Copyright: This module has been placed in the public domain.
"""
Simple HyperText Markup Language document tree Writer.
The output conforms to the HTML 4.01 Transitional DTD and to the Extensible
HTML version 1.0 Transitional DTD (*almost* strict). The output contains a
minimum of formatting information. A cascading style sheet ("default.css" by
default) is required for proper viewing with a modern graphical browser.
http://cvs.zope.org/Zope/lib/python/docutils/writers/Attic/html4zope.py?rev=1.1.2.2&only_with_tag=ajung-restructuredtext-integration-branch&content-type=text/vnd.viewcvs-markup
"""
__docformat__ = 'reStructuredText'
import os
from logilab.mtconverter import xml_escape
from docutils import nodes
from docutils.writers.html4css1 import Writer as CSS1Writer
from docutils.writers.html4css1 import HTMLTranslator as CSS1HTMLTranslator
default_level = int(os.environ.get('STX_DEFAULT_LEVEL', 3))
class Writer(CSS1Writer):
"""css writer using our html translator"""
def __init__(self, base_url):
CSS1Writer.__init__(self)
self.translator_class = URLBinder(base_url, HTMLTranslator)
def apply_template(self):
"""overriding this is necessary with docutils >= 0.5"""
return self.visitor.astext()
class URLBinder:
def __init__(self, url, klass):
self.base_url = url
self.translator_class = HTMLTranslator
def __call__(self, document):
translator = self.translator_class(document)
translator.base_url = self.base_url
return translator
class HTMLTranslator(CSS1HTMLTranslator):
"""ReST tree to html translator"""
def astext(self):
"""return the extracted html"""
return ''.join(self.body)
def visit_title(self, node):
"""Only 6 section levels are supported by HTML."""
if isinstance(node.parent, nodes.topic):
self.body.append(
self.starttag(node, 'p', '', CLASS='topic-title'))
if node.parent.hasattr('id'):
self.body.append(
self.starttag({}, 'a', '', name=node.parent['id']))
self.context.append('</a></p>\n')
else:
self.context.append('</p>\n')
elif self.section_level == 0:
# document title
self.head.append('<title>%s</title>\n'
% self.encode(node.astext()))
self.body.append(self.starttag(node, 'h%d' % default_level, '',
CLASS='title'))
self.context.append('</h%d>\n' % default_level)
else:
self.body.append(
self.starttag(node, 'h%s' % (
default_level+self.section_level-1), ''))
atts = {}
if node.hasattr('refid'):
atts['class'] = 'toc-backref'
atts['href'] = '%s#%s' % (self.base_url, node['refid'])
self.body.append(self.starttag({}, 'a', '', **atts))
self.context.append('</a></h%s>\n' % (
default_level+self.section_level-1))
def visit_subtitle(self, node):
"""format a subtitle"""
if isinstance(node.parent, nodes.sidebar):
self.body.append(self.starttag(node, 'p', '',
CLASS='sidebar-subtitle'))
self.context.append('</p>\n')
else:
self.body.append(
self.starttag(node, 'h%s' % (default_level+1), '',
CLASS='subtitle'))
self.context.append('</h%s>\n' % (default_level+1))
def visit_document(self, node):
"""syt: i don't want the enclosing <div class="document">"""
def depart_document(self, node):
"""syt: i don't want the enclosing <div class="document">"""
def visit_reference(self, node):
"""syt: i want absolute urls"""
if 'refuri' in node:
href = node['refuri']
if ( self.settings.cloak_email_addresses
and href.startswith('mailto:')):
href = self.cloak_mailto(href)
self.in_mailto = 1
else:
assert 'refid' in node, \
'References must have "refuri" or "refid" attribute.'
href = '%s#%s' % (self.base_url, node['refid'])
atts = {'href': href, 'class': 'reference'}
if not isinstance(node.parent, nodes.TextElement):
assert len(node) == 1 and isinstance(node[0], nodes.image)
atts['class'] += ' image-reference'
self.body.append(self.starttag(node, 'a', '', **atts))
## override error messages to avoid XHTML problems ########################
def visit_problematic(self, node):
pass
def depart_problematic(self, node):
pass
def visit_system_message(self, node):
backref_text = ''
if len(node['backrefs']):
backrefs = node['backrefs']
if len(backrefs) == 1:
backref_text = '; <em>backlink</em>'
else:
i = 1
backlinks = []
for backref in backrefs:
backlinks.append(str(i))
i += 1
backref_text = ('; <em>backlinks: %s</em>'
% ', '.join(backlinks))
if node.hasattr('line'):
line = ', line %s' % node['line']
else:
line = ''
a_start = a_end = ''
error = u'System Message: %s%s/%s%s (%s %s)%s</p>\n' % (
a_start, node['type'], node['level'], a_end,
self.encode(node['source']), line, backref_text)
self.body.append(u'<div class="system-message"><b>ReST / HTML errors:</b>%s</div>' % xml_escape(error))
def depart_system_message(self, node):
pass