# HG changeset patch # User sylvain.thenault@logilab.fr # Date 1234906458 -3600 # Node ID 0c2c8f0a6ded71f020ff20e18a3eaacc6fff9ba6 # Parent 413675c2d46c6784eb28cad90a2d3ceb4b8ed72a new ext package for modules depending on an option third party package diff -r 413675c2d46c -r 0c2c8f0a6ded common/html4zope.py --- a/common/html4zope.py Tue Feb 17 22:05:39 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,153 +0,0 @@ -# Author: David Goodger -# 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' - -from logilab.mtconverter import html_escape - -from docutils import nodes -from docutils.writers.html4css1 import Writer as CSS1Writer -from docutils.writers.html4css1 import HTMLTranslator as CSS1HTMLTranslator -import os - -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('

\n') - else: - self.context.append('

\n') - elif self.section_level == 0: - # document title - self.head.append('%s\n' - % self.encode(node.astext())) - self.body.append(self.starttag(node, 'h%d' % default_level, '', - CLASS='title')) - self.context.append('\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('\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('

\n') - else: - self.body.append( - self.starttag(node, 'h%s' % (default_level+1), '', - CLASS='subtitle')) - self.context.append('\n' % (default_level+1)) - - def visit_document(self, node): - """syt: i don't want the enclosing
""" - def depart_document(self, node): - """syt: i don't want the enclosing
""" - - def visit_reference(self, node): - """syt: i want absolute urls""" - if node.has_key('refuri'): - href = node['refuri'] - if ( self.settings.cloak_email_addresses - and href.startswith('mailto:')): - href = self.cloak_mailto(href) - self.in_mailto = 1 - else: - assert node.has_key('refid'), \ - '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 = '; backlink' - else: - i = 1 - backlinks = [] - for backref in backrefs: - backlinks.append(str(i)) - i += 1 - backref_text = ('; backlinks: %s' - % ', '.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

\n' % ( - a_start, node['type'], node['level'], a_end, - self.encode(node['source']), line, backref_text) - self.body.append(u'
ReST / HTML errors:%s
' % html_escape(error)) - - def depart_system_message(self, node): - pass diff -r 413675c2d46c -r 0c2c8f0a6ded common/mttransforms.py --- a/common/mttransforms.py Tue Feb 17 22:05:39 2009 +0100 +++ b/common/mttransforms.py Tue Feb 17 22:34:18 2009 +0100 @@ -31,16 +31,7 @@ output = 'text/html' def _convert(self, trdata): return html_publish(trdata.appobject, trdata.data) - -class ept_to_html(Transform): - inputs = ('text/cubicweb-page-template',) - output = 'text/html' - output_encoding = 'utf-8' - def _convert(self, trdata): - from cubicweb.common.tal import compile_template - value = trdata.encode(self.output_encoding) - return trdata.appobject.tal_render(compile_template(value), {}) - + # Instantiate and configure the transformation engine @@ -49,7 +40,23 @@ ENGINE = TransformEngine() ENGINE.add_transform(rest_to_html()) ENGINE.add_transform(html_to_html()) -ENGINE.add_transform(ept_to_html()) + +try: + from cubicweb.common.tal import compile_template +except ImportError: + HAS_TAL = False +else: + HAS_TAL = True + + class ept_to_html(Transform): + inputs = ('text/cubicweb-page-template',) + output = 'text/html' + output_encoding = 'utf-8' + def _convert(self, trdata): + value = trdata.encode(self.output_encoding) + return trdata.appobject.tal_render(compile_template(value), {}) + + ENGINE.add_transform(ept_to_html()) if register_pil_transforms(ENGINE, verb=False): HAS_PIL_TRANSFORMS = True diff -r 413675c2d46c -r 0c2c8f0a6ded common/rest.py --- a/common/rest.py Tue Feb 17 22:05:39 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,223 +0,0 @@ -"""rest publishing functions - -contains some functions and setup of docutils for cubicweb - -:organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr -""" -__docformat__ = "restructuredtext en" - -from cStringIO import StringIO -from itertools import chain -from logging import getLogger -from os.path import join - -from docutils import statemachine, nodes, utils, io -from docutils.core import publish_string -from docutils.parsers.rst import Parser, states, directives -from docutils.parsers.rst.roles import register_canonical_role, set_classes - -from logilab.mtconverter import html_escape - -from cubicweb.common.html4zope import Writer - -# We provide our own parser as an attempt to get rid of -# state machine reinstanciation - -import re -# compile states.Body patterns -for k, v in states.Body.patterns.items(): - if isinstance(v, str): - states.Body.patterns[k] = re.compile(v) - -# register ReStructured Text mimetype / extensions -import mimetypes -mimetypes.add_type('text/rest', '.rest') -mimetypes.add_type('text/rest', '.rst') - - -LOGGER = getLogger('cubicweb.rest') - -def eid_reference_role(role, rawtext, text, lineno, inliner, - options={}, content=[]): - try: - try: - eid_num, rest = text.split(u':', 1) - except: - eid_num, rest = text, '#'+text - eid_num = int(eid_num) - if eid_num < 0: - raise ValueError - except ValueError: - msg = inliner.reporter.error( - 'EID number must be a positive number; "%s" is invalid.' - % text, line=lineno) - prb = inliner.problematic(rawtext, rawtext, msg) - return [prb], [msg] - # Base URL mainly used by inliner.pep_reference; so this is correct: - context = inliner.document.settings.context - refedentity = context.req.eid_rset(eid_num).get_entity(0, 0) - ref = refedentity.absolute_url() - set_classes(options) - return [nodes.reference(rawtext, utils.unescape(rest), refuri=ref, - **options)], [] - -register_canonical_role('eid', eid_reference_role) - - -def card_reference_role(role, rawtext, text, lineno, inliner, - options={}, content=[]): - text = text.strip() - try: - wikiid, rest = text.split(u':', 1) - except: - wikiid, rest = text, text - context = inliner.document.settings.context - cardrset = context.req.execute('Card X WHERE X wikiid %(id)s', - {'id': wikiid}) - if cardrset: - ref = cardrset.get_entity(0, 0).absolute_url() - else: - schema = context.schema - if schema.eschema('Card').has_perm(context.req, 'add'): - ref = context.req.build_url('view', vid='creation', etype='Card', wikiid=wikiid) - else: - ref = '#' - set_classes(options) - return [nodes.reference(rawtext, utils.unescape(rest), refuri=ref, - **options)], [] - -register_canonical_role('card', card_reference_role) - - -def winclude_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - """Include a reST file as part of the content of this reST file. - - same as standard include directive but using config.locate_doc_resource to - get actual file to include. - - Most part of this implementation is copied from `include` directive defined - in `docutils.parsers.rst.directives.misc` - """ - context = state.document.settings.context - source = state_machine.input_lines.source( - lineno - state_machine.input_offset - 1) - #source_dir = os.path.dirname(os.path.abspath(source)) - fid = arguments[0] - for lang in chain((context.req.lang, context.vreg.property_value('ui.language')), - context.config.available_languages()): - rid = '%s_%s.rst' % (fid, lang) - resourcedir = context.config.locate_doc_file(rid) - if resourcedir: - break - else: - severe = state_machine.reporter.severe( - 'Problems with "%s" directive path:\nno resource matching %s.' - % (name, fid), - nodes.literal_block(block_text, block_text), line=lineno) - return [severe] - path = join(resourcedir, rid) - encoding = options.get('encoding', state.document.settings.input_encoding) - try: - state.document.settings.record_dependencies.add(path) - include_file = io.FileInput( - source_path=path, encoding=encoding, - error_handler=state.document.settings.input_encoding_error_handler, - handle_io_errors=None) - except IOError, error: - severe = state_machine.reporter.severe( - 'Problems with "%s" directive path:\n%s: %s.' - % (name, error.__class__.__name__, error), - nodes.literal_block(block_text, block_text), line=lineno) - return [severe] - try: - include_text = include_file.read() - except UnicodeError, error: - severe = state_machine.reporter.severe( - 'Problem with "%s" directive:\n%s: %s' - % (name, error.__class__.__name__, error), - nodes.literal_block(block_text, block_text), line=lineno) - return [severe] - if options.has_key('literal'): - literal_block = nodes.literal_block(include_text, include_text, - source=path) - literal_block.line = 1 - return literal_block - else: - include_lines = statemachine.string2lines(include_text, - convert_whitespace=1) - state_machine.insert_input(include_lines, path) - return [] - -winclude_directive.arguments = (1, 0, 1) -winclude_directive.options = {'literal': directives.flag, - 'encoding': directives.encoding} -directives.register_directive('winclude', winclude_directive) - -class CubicWebReSTParser(Parser): - """The (customized) reStructuredText parser.""" - - def __init__(self): - self.initial_state = 'Body' - self.state_classes = states.state_classes - self.inliner = states.Inliner() - self.statemachine = states.RSTStateMachine( - state_classes=self.state_classes, - initial_state=self.initial_state, - debug=0) - - def parse(self, inputstring, document): - """Parse `inputstring` and populate `document`, a document tree.""" - self.setup_parse(inputstring, document) - inputlines = statemachine.string2lines(inputstring, - convert_whitespace=1) - self.statemachine.run(inputlines, document, inliner=self.inliner) - self.finish_parse() - - -_REST_PARSER = CubicWebReSTParser() - -def rest_publish(context, data): - """publish a string formatted as ReStructured Text to HTML - - :type context: a cubicweb application object - - :type data: str - :param data: some ReST text - - :rtype: unicode - :return: - the data formatted as HTML or the original data if an error occured - """ - req = context.req - if isinstance(data, unicode): - encoding = 'unicode' - else: - encoding = req.encoding - settings = {'input_encoding': encoding, 'output_encoding': 'unicode', - 'warning_stream': StringIO(), 'context': context, - # dunno what's the max, severe is 4, and we never want a crash - # (though try/except may be a better option...) - 'halt_level': 10, - } - if context: - if hasattr(req, 'url'): - base_url = req.url() - elif hasattr(context, 'absolute_url'): - base_url = context.absolute_url() - else: - base_url = req.base_url() - else: - base_url = None - try: - return publish_string(writer=Writer(base_url=base_url), - parser=_REST_PARSER, source=data, - settings_overrides=settings) - except Exception: - LOGGER.exception('error while publishing ReST text') - if not isinstance(data, unicode): - data = unicode(data, encoding, 'replace') - return html_escape(req._('error while publishing ReST text') - + '\n\n' + data) diff -r 413675c2d46c -r 0c2c8f0a6ded common/tal.py --- a/common/tal.py Tue Feb 17 22:05:39 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,256 +0,0 @@ -"""provides simpleTAL extensions for CubicWeb - -:organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr -""" - -__docformat__ = "restructuredtext en" - -import sys -import re -from os.path import exists, isdir, join -from logging import getLogger -from StringIO import StringIO - -from simpletal import simpleTAL, simpleTALES - -from logilab.common.decorators import cached - -LOGGER = getLogger('cubicweb.tal') - - -class LoggerAdapter(object): - def __init__(self, tal_logger): - self.tal_logger = tal_logger - - def debug(self, msg): - LOGGER.debug(msg) - - def warn(self, msg): - LOGGER.warning(msg) - - def __getattr__(self, attrname): - return getattr(self.tal_logger, attrname) - - -class CubicWebContext(simpleTALES.Context): - """add facilities to access entity / resultset""" - - def __init__(self, options=None, allowPythonPath=1): - simpleTALES.Context.__init__(self, options, allowPythonPath) - self.log = LoggerAdapter(self.log) - - def update(self, context): - for varname, value in context.items(): - self.addGlobal(varname, value) - - def addRepeat(self, name, var, initialValue): - simpleTALES.Context.addRepeat(self, name, var, initialValue) - -# XXX FIXME need to find a clean to define OPCODE values for extensions -I18N_CONTENT = 18 -I18N_REPLACE = 19 -RQL_EXECUTE = 20 -# simpleTAL uses the OPCODE values to define priority over commands. -# TAL_ITER should have the same priority than TAL_REPEAT (i.e. 3), but -# we can't use the same OPCODE for two different commands without changing -# the simpleTAL implementation. Another solution would be to totally override -# the REPEAT implementation with the ITER one, but some specific operations -# (involving len() for instance) are not implemented for ITER, so we prefer -# to keep both implementations for now, and to fool simpleTAL by using a float -# number between 3 and 4 -TAL_ITER = 3.1 - - -# FIX simpleTAL HTML 4.01 stupidity -# (simpleTAL never closes tags like INPUT, IMG, HR ...) -simpleTAL.HTML_FORBIDDEN_ENDTAG.clear() - -class CubicWebTemplateCompiler(simpleTAL.HTMLTemplateCompiler): - """extends default compiler by adding i18n:content commands""" - - def __init__(self): - simpleTAL.HTMLTemplateCompiler.__init__(self) - self.commandHandler[I18N_CONTENT] = self.compile_cmd_i18n_content - self.commandHandler[I18N_REPLACE] = self.compile_cmd_i18n_replace - self.commandHandler[RQL_EXECUTE] = self.compile_cmd_rql - self.commandHandler[TAL_ITER] = self.compile_cmd_tal_iter - - def setTALPrefix(self, prefix): - simpleTAL.TemplateCompiler.setTALPrefix(self, prefix) - self.tal_attribute_map['i18n:content'] = I18N_CONTENT - self.tal_attribute_map['i18n:replace'] = I18N_REPLACE - self.tal_attribute_map['rql:execute'] = RQL_EXECUTE - self.tal_attribute_map['tal:iter'] = TAL_ITER - - def compile_cmd_i18n_content(self, argument): - # XXX tal:content structure=, text= should we support this ? - structure_flag = 0 - return (I18N_CONTENT, (argument, False, structure_flag, self.endTagSymbol)) - - def compile_cmd_i18n_replace(self, argument): - # XXX tal:content structure=, text= should we support this ? - structure_flag = 0 - return (I18N_CONTENT, (argument, True, structure_flag, self.endTagSymbol)) - - def compile_cmd_rql(self, argument): - return (RQL_EXECUTE, (argument, self.endTagSymbol)) - - def compile_cmd_tal_iter(self, argument): - original_id, (var_name, expression, end_tag_symbol) = \ - simpleTAL.HTMLTemplateCompiler.compileCmdRepeat(self, argument) - return (TAL_ITER, (var_name, expression, self.endTagSymbol)) - - def getTemplate(self): - return CubicWebTemplate(self.commandList, self.macroMap, self.symbolLocationTable) - - def compileCmdAttributes (self, argument): - """XXX modified to support single attribute - definition ending by a ';' - - backport this to simpleTAL - """ - # Compile tal:attributes into attribute command - # Argument: [(attributeName, expression)] - - # Break up the list of attribute settings first - commandArgs = [] - # We only want to match semi-colons that are not escaped - argumentSplitter = re.compile(r'(?= 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('

\n') + else: + self.context.append('

\n') + elif self.section_level == 0: + # document title + self.head.append('%s\n' + % self.encode(node.astext())) + self.body.append(self.starttag(node, 'h%d' % default_level, '', + CLASS='title')) + self.context.append('\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('\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('

\n') + else: + self.body.append( + self.starttag(node, 'h%s' % (default_level+1), '', + CLASS='subtitle')) + self.context.append('\n' % (default_level+1)) + + def visit_document(self, node): + """syt: i don't want the enclosing
""" + def depart_document(self, node): + """syt: i don't want the enclosing
""" + + def visit_reference(self, node): + """syt: i want absolute urls""" + if node.has_key('refuri'): + href = node['refuri'] + if ( self.settings.cloak_email_addresses + and href.startswith('mailto:')): + href = self.cloak_mailto(href) + self.in_mailto = 1 + else: + assert node.has_key('refid'), \ + '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 = '; backlink' + else: + i = 1 + backlinks = [] + for backref in backrefs: + backlinks.append(str(i)) + i += 1 + backref_text = ('; backlinks: %s' + % ', '.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

\n' % ( + a_start, node['type'], node['level'], a_end, + self.encode(node['source']), line, backref_text) + self.body.append(u'
ReST / HTML errors:%s
' % html_escape(error)) + + def depart_system_message(self, node): + pass diff -r 413675c2d46c -r 0c2c8f0a6ded ext/rest.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ext/rest.py Tue Feb 17 22:34:18 2009 +0100 @@ -0,0 +1,223 @@ +"""rest publishing functions + +contains some functions and setup of docutils for cubicweb + +:organization: Logilab +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr +""" +__docformat__ = "restructuredtext en" + +from cStringIO import StringIO +from itertools import chain +from logging import getLogger +from os.path import join + +from docutils import statemachine, nodes, utils, io +from docutils.core import publish_string +from docutils.parsers.rst import Parser, states, directives +from docutils.parsers.rst.roles import register_canonical_role, set_classes + +from logilab.mtconverter import html_escape + +from cubicweb.ext.html4zope import Writer + +# We provide our own parser as an attempt to get rid of +# state machine reinstanciation + +import re +# compile states.Body patterns +for k, v in states.Body.patterns.items(): + if isinstance(v, str): + states.Body.patterns[k] = re.compile(v) + +# register ReStructured Text mimetype / extensions +import mimetypes +mimetypes.add_type('text/rest', '.rest') +mimetypes.add_type('text/rest', '.rst') + + +LOGGER = getLogger('cubicweb.rest') + +def eid_reference_role(role, rawtext, text, lineno, inliner, + options={}, content=[]): + try: + try: + eid_num, rest = text.split(u':', 1) + except: + eid_num, rest = text, '#'+text + eid_num = int(eid_num) + if eid_num < 0: + raise ValueError + except ValueError: + msg = inliner.reporter.error( + 'EID number must be a positive number; "%s" is invalid.' + % text, line=lineno) + prb = inliner.problematic(rawtext, rawtext, msg) + return [prb], [msg] + # Base URL mainly used by inliner.pep_reference; so this is correct: + context = inliner.document.settings.context + refedentity = context.req.eid_rset(eid_num).get_entity(0, 0) + ref = refedentity.absolute_url() + set_classes(options) + return [nodes.reference(rawtext, utils.unescape(rest), refuri=ref, + **options)], [] + +register_canonical_role('eid', eid_reference_role) + + +def card_reference_role(role, rawtext, text, lineno, inliner, + options={}, content=[]): + text = text.strip() + try: + wikiid, rest = text.split(u':', 1) + except: + wikiid, rest = text, text + context = inliner.document.settings.context + cardrset = context.req.execute('Card X WHERE X wikiid %(id)s', + {'id': wikiid}) + if cardrset: + ref = cardrset.get_entity(0, 0).absolute_url() + else: + schema = context.schema + if schema.eschema('Card').has_perm(context.req, 'add'): + ref = context.req.build_url('view', vid='creation', etype='Card', wikiid=wikiid) + else: + ref = '#' + set_classes(options) + return [nodes.reference(rawtext, utils.unescape(rest), refuri=ref, + **options)], [] + +register_canonical_role('card', card_reference_role) + + +def winclude_directive(name, arguments, options, content, lineno, + content_offset, block_text, state, state_machine): + """Include a reST file as part of the content of this reST file. + + same as standard include directive but using config.locate_doc_resource to + get actual file to include. + + Most part of this implementation is copied from `include` directive defined + in `docutils.parsers.rst.directives.misc` + """ + context = state.document.settings.context + source = state_machine.input_lines.source( + lineno - state_machine.input_offset - 1) + #source_dir = os.path.dirname(os.path.abspath(source)) + fid = arguments[0] + for lang in chain((context.req.lang, context.vreg.property_value('ui.language')), + context.config.available_languages()): + rid = '%s_%s.rst' % (fid, lang) + resourcedir = context.config.locate_doc_file(rid) + if resourcedir: + break + else: + severe = state_machine.reporter.severe( + 'Problems with "%s" directive path:\nno resource matching %s.' + % (name, fid), + nodes.literal_block(block_text, block_text), line=lineno) + return [severe] + path = join(resourcedir, rid) + encoding = options.get('encoding', state.document.settings.input_encoding) + try: + state.document.settings.record_dependencies.add(path) + include_file = io.FileInput( + source_path=path, encoding=encoding, + error_handler=state.document.settings.input_encoding_error_handler, + handle_io_errors=None) + except IOError, error: + severe = state_machine.reporter.severe( + 'Problems with "%s" directive path:\n%s: %s.' + % (name, error.__class__.__name__, error), + nodes.literal_block(block_text, block_text), line=lineno) + return [severe] + try: + include_text = include_file.read() + except UnicodeError, error: + severe = state_machine.reporter.severe( + 'Problem with "%s" directive:\n%s: %s' + % (name, error.__class__.__name__, error), + nodes.literal_block(block_text, block_text), line=lineno) + return [severe] + if options.has_key('literal'): + literal_block = nodes.literal_block(include_text, include_text, + source=path) + literal_block.line = 1 + return literal_block + else: + include_lines = statemachine.string2lines(include_text, + convert_whitespace=1) + state_machine.insert_input(include_lines, path) + return [] + +winclude_directive.arguments = (1, 0, 1) +winclude_directive.options = {'literal': directives.flag, + 'encoding': directives.encoding} +directives.register_directive('winclude', winclude_directive) + +class CubicWebReSTParser(Parser): + """The (customized) reStructuredText parser.""" + + def __init__(self): + self.initial_state = 'Body' + self.state_classes = states.state_classes + self.inliner = states.Inliner() + self.statemachine = states.RSTStateMachine( + state_classes=self.state_classes, + initial_state=self.initial_state, + debug=0) + + def parse(self, inputstring, document): + """Parse `inputstring` and populate `document`, a document tree.""" + self.setup_parse(inputstring, document) + inputlines = statemachine.string2lines(inputstring, + convert_whitespace=1) + self.statemachine.run(inputlines, document, inliner=self.inliner) + self.finish_parse() + + +_REST_PARSER = CubicWebReSTParser() + +def rest_publish(context, data): + """publish a string formatted as ReStructured Text to HTML + + :type context: a cubicweb application object + + :type data: str + :param data: some ReST text + + :rtype: unicode + :return: + the data formatted as HTML or the original data if an error occured + """ + req = context.req + if isinstance(data, unicode): + encoding = 'unicode' + else: + encoding = req.encoding + settings = {'input_encoding': encoding, 'output_encoding': 'unicode', + 'warning_stream': StringIO(), 'context': context, + # dunno what's the max, severe is 4, and we never want a crash + # (though try/except may be a better option...) + 'halt_level': 10, + } + if context: + if hasattr(req, 'url'): + base_url = req.url() + elif hasattr(context, 'absolute_url'): + base_url = context.absolute_url() + else: + base_url = req.base_url() + else: + base_url = None + try: + return publish_string(writer=Writer(base_url=base_url), + parser=_REST_PARSER, source=data, + settings_overrides=settings) + except Exception: + LOGGER.exception('error while publishing ReST text') + if not isinstance(data, unicode): + data = unicode(data, encoding, 'replace') + return html_escape(req._('error while publishing ReST text') + + '\n\n' + data) diff -r 413675c2d46c -r 0c2c8f0a6ded ext/tal.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ext/tal.py Tue Feb 17 22:34:18 2009 +0100 @@ -0,0 +1,256 @@ +"""provides simpleTAL extensions for CubicWeb + +:organization: Logilab +:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr +""" + +__docformat__ = "restructuredtext en" + +import sys +import re +from os.path import exists, isdir, join +from logging import getLogger +from StringIO import StringIO + +from simpletal import simpleTAL, simpleTALES + +from logilab.common.decorators import cached + +LOGGER = getLogger('cubicweb.tal') + + +class LoggerAdapter(object): + def __init__(self, tal_logger): + self.tal_logger = tal_logger + + def debug(self, msg): + LOGGER.debug(msg) + + def warn(self, msg): + LOGGER.warning(msg) + + def __getattr__(self, attrname): + return getattr(self.tal_logger, attrname) + + +class CubicWebContext(simpleTALES.Context): + """add facilities to access entity / resultset""" + + def __init__(self, options=None, allowPythonPath=1): + simpleTALES.Context.__init__(self, options, allowPythonPath) + self.log = LoggerAdapter(self.log) + + def update(self, context): + for varname, value in context.items(): + self.addGlobal(varname, value) + + def addRepeat(self, name, var, initialValue): + simpleTALES.Context.addRepeat(self, name, var, initialValue) + +# XXX FIXME need to find a clean to define OPCODE values for extensions +I18N_CONTENT = 18 +I18N_REPLACE = 19 +RQL_EXECUTE = 20 +# simpleTAL uses the OPCODE values to define priority over commands. +# TAL_ITER should have the same priority than TAL_REPEAT (i.e. 3), but +# we can't use the same OPCODE for two different commands without changing +# the simpleTAL implementation. Another solution would be to totally override +# the REPEAT implementation with the ITER one, but some specific operations +# (involving len() for instance) are not implemented for ITER, so we prefer +# to keep both implementations for now, and to fool simpleTAL by using a float +# number between 3 and 4 +TAL_ITER = 3.1 + + +# FIX simpleTAL HTML 4.01 stupidity +# (simpleTAL never closes tags like INPUT, IMG, HR ...) +simpleTAL.HTML_FORBIDDEN_ENDTAG.clear() + +class CubicWebTemplateCompiler(simpleTAL.HTMLTemplateCompiler): + """extends default compiler by adding i18n:content commands""" + + def __init__(self): + simpleTAL.HTMLTemplateCompiler.__init__(self) + self.commandHandler[I18N_CONTENT] = self.compile_cmd_i18n_content + self.commandHandler[I18N_REPLACE] = self.compile_cmd_i18n_replace + self.commandHandler[RQL_EXECUTE] = self.compile_cmd_rql + self.commandHandler[TAL_ITER] = self.compile_cmd_tal_iter + + def setTALPrefix(self, prefix): + simpleTAL.TemplateCompiler.setTALPrefix(self, prefix) + self.tal_attribute_map['i18n:content'] = I18N_CONTENT + self.tal_attribute_map['i18n:replace'] = I18N_REPLACE + self.tal_attribute_map['rql:execute'] = RQL_EXECUTE + self.tal_attribute_map['tal:iter'] = TAL_ITER + + def compile_cmd_i18n_content(self, argument): + # XXX tal:content structure=, text= should we support this ? + structure_flag = 0 + return (I18N_CONTENT, (argument, False, structure_flag, self.endTagSymbol)) + + def compile_cmd_i18n_replace(self, argument): + # XXX tal:content structure=, text= should we support this ? + structure_flag = 0 + return (I18N_CONTENT, (argument, True, structure_flag, self.endTagSymbol)) + + def compile_cmd_rql(self, argument): + return (RQL_EXECUTE, (argument, self.endTagSymbol)) + + def compile_cmd_tal_iter(self, argument): + original_id, (var_name, expression, end_tag_symbol) = \ + simpleTAL.HTMLTemplateCompiler.compileCmdRepeat(self, argument) + return (TAL_ITER, (var_name, expression, self.endTagSymbol)) + + def getTemplate(self): + return CubicWebTemplate(self.commandList, self.macroMap, self.symbolLocationTable) + + def compileCmdAttributes (self, argument): + """XXX modified to support single attribute + definition ending by a ';' + + backport this to simpleTAL + """ + # Compile tal:attributes into attribute command + # Argument: [(attributeName, expression)] + + # Break up the list of attribute settings first + commandArgs = [] + # We only want to match semi-colons that are not escaped + argumentSplitter = re.compile(r'(?