"""rest publishing functionscontains some functions and setup of docutils for cubicweb. Provides thefollowing ReST directives:* `eid`, create link to entity in the repository by their eid* `card`, create link to card entity in the repository by their wikiid (proposing to create it when the refered card doesn't exist yet)* `winclude`, reference to a web documentation file (in wdoc/ directories)* `sourcecode` (if pygments is installed), source code colorization:organization: Logilab:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses"""__docformat__="restructuredtext en"fromcStringIOimportStringIOfromitertoolsimportchainfromloggingimportgetLoggerfromos.pathimportjoinfromdocutilsimportstatemachine,nodes,utils,iofromdocutils.coreimportpublish_stringfromdocutils.parsers.rstimportParser,states,directivesfromdocutils.parsers.rst.rolesimportregister_canonical_role,set_classesfromlogilab.mtconverterimportESC_UCAR_TABLE,ESC_CAR_TABLE,xml_escapefromcubicwebimportUnknownEidfromcubicweb.ext.html4zopeimportWriter# We provide our own parser as an attempt to get rid of# state machine reinstanciationimportre# compile states.Body patternsfork,vinstates.Body.patterns.items():ifisinstance(v,str):states.Body.patterns[k]=re.compile(v)# register ReStructured Text mimetype / extensionsimportmimetypesmimetypes.add_type('text/rest','.rest')mimetypes.add_type('text/rest','.rst')LOGGER=getLogger('cubicweb.rest')defeid_reference_role(role,rawtext,text,lineno,inliner,options={},content=[]):try:try:eid_num,rest=text.split(u':',1)except:eid_num,rest=text,'#'+texteid_num=int(eid_num)ifeid_num<0:raiseValueErrorexceptValueError: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.contexttry:refedentity=context.req.entity_from_eid(eid_num)exceptUnknownEid:ref='#'rest+=u' '+context.req._('(UNEXISTANT EID)')else:ref=refedentity.absolute_url()set_classes(options)return[nodes.reference(rawtext,utils.unescape(rest),refuri=ref,**options)],[]register_canonical_role('eid',eid_reference_role)defwinclude_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.contextsource=state_machine.input_lines.source(lineno-state_machine.input_offset-1)#source_dir = os.path.dirname(os.path.abspath(source))fid=arguments[0]forlanginchain((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)ifresourcedir:breakelse: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)exceptIOError,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()exceptUnicodeError,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]ifoptions.has_key('literal'):literal_block=nodes.literal_block(include_text,include_text,source=path)literal_block.line=1returnliteral_blockelse: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)try:frompygmentsimporthighlightfrompygments.lexersimportget_lexer_by_name,LEXERSfrompygments.formattersimportHtmlFormatterexceptImportError:passelse:_PYGMENTS_FORMATTER=HtmlFormatter()defpygments_directive(name,arguments,options,content,lineno,content_offset,block_text,state,state_machine):try:lexer=get_lexer_by_name(arguments[0])exceptValueError:importtracebacktraceback.print_exc()printsorted(aliasesformodule_name,name,aliases,_,_inLEXERS.itervalues())# no lexer foundlexer=get_lexer_by_name('text')parsed=highlight(u'\n'.join(content),lexer,_PYGMENTS_FORMATTER)context=state.document.settings.contextcontext.req.add_css('pygments.css')return[nodes.raw('',parsed,format='html')]pygments_directive.arguments=(1,0,1)pygments_directive.content=1directives.register_directive('sourcecode',pygments_directive)classCubicWebReSTParser(Parser):"""The (customized) reStructuredText parser."""def__init__(self):self.initial_state='Body'self.state_classes=states.state_classesself.inliner=states.Inliner()self.statemachine=states.RSTStateMachine(state_classes=self.state_classes,initial_state=self.initial_state,debug=0)defparse(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()defrest_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.reqifisinstance(data,unicode):encoding='unicode'# remove unprintable characters unauthorized in xmldata=data.translate(ESC_UCAR_TABLE)else:encoding=req.encoding# remove unprintable characters unauthorized in xmldata=data.translate(ESC_CAR_TABLE)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,}ifcontext:ifhasattr(req,'url'):base_url=req.url()elifhasattr(context,'absolute_url'):base_url=context.absolute_url()else:base_url=req.base_url()else:base_url=Nonetry:returnpublish_string(writer=Writer(base_url=base_url),parser=CubicWebReSTParser(),source=data,settings_overrides=settings)exceptException:LOGGER.exception('error while publishing ReST text')ifnotisinstance(data,unicode):data=unicode(data,encoding,'replace')returnxml_escape(req._('error while publishing ReST text')+'\n\n'+data)