|
1 # copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
|
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
3 # |
|
4 # This file is part of CubicWeb. |
|
5 # |
|
6 # CubicWeb is free software: you can redistribute it and/or modify it under the |
|
7 # terms of the GNU Lesser General Public License as published by the Free |
|
8 # Software Foundation, either version 2.1 of the License, or (at your option) |
|
9 # any later version. |
|
10 # |
|
11 # logilab-common is distributed in the hope that it will be useful, but WITHOUT |
|
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
|
13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
|
14 # details. |
|
15 # |
|
16 # You should have received a copy of the GNU Lesser General Public License along |
|
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
1 """rest publishing functions |
18 """rest publishing functions |
2 |
19 |
3 contains some functions and setup of docutils for cubicweb. Provides the |
20 contains some functions and setup of docutils for cubicweb. Provides the |
4 following ReST directives: |
21 following ReST directives: |
5 |
22 |
10 |
27 |
11 * `winclude`, reference to a web documentation file (in wdoc/ directories) |
28 * `winclude`, reference to a web documentation file (in wdoc/ directories) |
12 |
29 |
13 * `sourcecode` (if pygments is installed), source code colorization |
30 * `sourcecode` (if pygments is installed), source code colorization |
14 |
31 |
15 :organization: Logilab |
|
16 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. |
|
17 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
18 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
|
19 """ |
32 """ |
20 __docformat__ = "restructuredtext en" |
33 __docformat__ = "restructuredtext en" |
21 |
34 |
22 from cStringIO import StringIO |
35 from cStringIO import StringIO |
23 from itertools import chain |
36 from itertools import chain |
24 from logging import getLogger |
37 from logging import getLogger |
25 from os.path import join |
38 from os.path import join |
26 |
39 |
27 from docutils import statemachine, nodes, utils, io |
40 from docutils import statemachine, nodes, utils, io |
28 from docutils.core import publish_string |
41 from docutils.core import Publisher |
29 from docutils.parsers.rst import Parser, states, directives |
42 from docutils.parsers.rst import Parser, states, directives |
30 from docutils.parsers.rst.roles import register_canonical_role, set_classes |
43 from docutils.parsers.rst.roles import register_canonical_role, set_classes |
31 |
44 |
32 from logilab.mtconverter import ESC_UCAR_TABLE, ESC_CAR_TABLE, xml_escape |
45 from logilab.mtconverter import ESC_UCAR_TABLE, ESC_CAR_TABLE, xml_escape |
33 |
46 |
90 |
103 |
91 Most part of this implementation is copied from `include` directive defined |
104 Most part of this implementation is copied from `include` directive defined |
92 in `docutils.parsers.rst.directives.misc` |
105 in `docutils.parsers.rst.directives.misc` |
93 """ |
106 """ |
94 context = state.document.settings.context |
107 context = state.document.settings.context |
|
108 cw = context._cw |
95 source = state_machine.input_lines.source( |
109 source = state_machine.input_lines.source( |
96 lineno - state_machine.input_offset - 1) |
110 lineno - state_machine.input_offset - 1) |
97 #source_dir = os.path.dirname(os.path.abspath(source)) |
111 #source_dir = os.path.dirname(os.path.abspath(source)) |
98 fid = arguments[0] |
112 fid = arguments[0] |
99 for lang in chain((context._cw.lang, context.vreg.property_value('ui.language')), |
113 for lang in chain((cw.lang, cw.vreg.property_value('ui.language')), |
100 context.config.available_languages()): |
114 cw.vreg.config.available_languages()): |
101 rid = '%s_%s.rst' % (fid, lang) |
115 rid = '%s_%s.rst' % (fid, lang) |
102 resourcedir = context.config.locate_doc_file(rid) |
116 resourcedir = cw.vreg.config.locate_doc_file(rid) |
103 if resourcedir: |
117 if resourcedir: |
104 break |
118 break |
105 else: |
119 else: |
106 severe = state_machine.reporter.severe( |
120 severe = state_machine.reporter.severe( |
107 'Problems with "%s" directive path:\nno resource matching %s.' |
121 'Problems with "%s" directive path:\nno resource matching %s.' |
194 convert_whitespace=1) |
208 convert_whitespace=1) |
195 self.statemachine.run(inputlines, document, inliner=self.inliner) |
209 self.statemachine.run(inputlines, document, inliner=self.inliner) |
196 self.finish_parse() |
210 self.finish_parse() |
197 |
211 |
198 |
212 |
|
213 # XXX docutils keep a ref on context, can't find a correct way to remove it |
|
214 class CWReSTPublisher(Publisher): |
|
215 def __init__(self, context, settings, **kwargs): |
|
216 Publisher.__init__(self, **kwargs) |
|
217 self.set_components('standalone', 'restructuredtext', 'pseudoxml') |
|
218 self.process_programmatic_settings(None, settings, None) |
|
219 self.settings.context = context |
|
220 |
|
221 |
199 def rest_publish(context, data): |
222 def rest_publish(context, data): |
200 """publish a string formatted as ReStructured Text to HTML |
223 """publish a string formatted as ReStructured Text to HTML |
201 |
224 |
202 :type context: a cubicweb application object |
225 :type context: a cubicweb application object |
203 |
226 |
216 else: |
239 else: |
217 encoding = req.encoding |
240 encoding = req.encoding |
218 # remove unprintable characters unauthorized in xml |
241 # remove unprintable characters unauthorized in xml |
219 data = data.translate(ESC_CAR_TABLE) |
242 data = data.translate(ESC_CAR_TABLE) |
220 settings = {'input_encoding': encoding, 'output_encoding': 'unicode', |
243 settings = {'input_encoding': encoding, 'output_encoding': 'unicode', |
221 'warning_stream': StringIO(), 'context': context, |
244 'warning_stream': StringIO(), |
222 # dunno what's the max, severe is 4, and we never want a crash |
245 # dunno what's the max, severe is 4, and we never want a crash |
223 # (though try/except may be a better option...) |
246 # (though try/except may be a better option...) |
224 'halt_level': 10, |
247 'halt_level': 10, |
225 } |
248 } |
226 if context: |
249 if context: |
231 else: |
254 else: |
232 base_url = req.base_url() |
255 base_url = req.base_url() |
233 else: |
256 else: |
234 base_url = None |
257 base_url = None |
235 try: |
258 try: |
236 return publish_string(writer=Writer(base_url=base_url), |
259 pub = CWReSTPublisher(context, settings, |
237 parser=CubicWebReSTParser(), source=data, |
260 parser=CubicWebReSTParser(), |
238 settings_overrides=settings) |
261 writer=Writer(base_url=base_url), |
|
262 source_class=io.StringInput, |
|
263 destination_class=io.StringOutput) |
|
264 pub.set_source(data) |
|
265 pub.set_destination() |
|
266 res = pub.publish(enable_exit_status=None) |
|
267 # necessary for proper garbage collection, else a ref is kept somewhere in docutils... |
|
268 del pub.settings.context |
|
269 return res |
239 except Exception: |
270 except Exception: |
240 LOGGER.exception('error while publishing ReST text') |
271 LOGGER.exception('error while publishing ReST text') |
241 if not isinstance(data, unicode): |
272 if not isinstance(data, unicode): |
242 data = unicode(data, encoding, 'replace') |
273 data = unicode(data, encoding, 'replace') |
243 return xml_escape(req._('error while publishing ReST text') |
274 return xml_escape(req._('error while publishing ReST text') |