[ext/rest] add directive bookmark to rest (closes #2545595)

authorNicolas Chauvat <nicolas.chauvat@logilab.fr>
changeset59a29405688c
branchdefault
phasepublic
hiddenno
parent revision#0020aa12af07 [c-c create] make post-create step optional (closes #2712041)
child revision#e81bc7300a8b [devtool] randomise available port search in http test, #6ad000b91347 merge 3.16.x fix in 3.17.x branch
files modified by this revision
doc/3.17.rst
doc/book/en/tutorials/index.rst
doc/book/en/tutorials/textreports/index.rst
ext/rest.py
ext/test/unittest_rest.py
# HG changeset patch
# User Nicolas Chauvat <nicolas.chauvat@logilab.fr>
# Date 1365090309 -7200
# Thu Apr 04 17:45:09 2013 +0200
# Node ID 59a29405688c4f71fe5ecf19da7d5ab9ed672052
# Parent 0020aa12af07a45200041ac858a0becf8e2330c3
[ext/rest] add directive bookmark to rest (closes #2545595)

diff --git a/doc/3.17.rst b/doc/3.17.rst
@@ -8,10 +8,13 @@
1    (see `#464991 <http://www.cubicweb.org/464991>`_)
2 
3  * Add CubicWebRequestBase.content with the content of the HTTP request (see #2742453)
4    (see `#2742453 <http://www.cubicweb.org/2742453>`_)
5 
6 +* Add directive bookmark to ReST rendering
7 +  (see `#2545595 <http://www.cubicweb.org/ticket/2545595>`_)
8 +
9 
10  API changes
11  -----------
12 
13  * drop typed_eid() in favour of int() (see `#2742462 <http://www.cubicweb.org/2742462>`_)
diff --git a/doc/book/en/tutorials/index.rst b/doc/book/en/tutorials/index.rst
@@ -16,5 +16,6 @@
14     :numbered:
15 
16     base/index
17     advanced/index
18     tools/windmill.rst
19 +   textreports/index
diff --git a/doc/book/en/tutorials/textreports/index.rst b/doc/book/en/tutorials/textreports/index.rst
@@ -0,0 +1,13 @@
20 +.. -*- coding: utf-8 -*-
21 +
22 +Writing text reports with RestructuredText
23 +==========================================
24 +
25 +|cubicweb| offers several text formats for the RichString type used in schemas,
26 +including restructuredtext.
27 +
28 +Three additional restructuredtext roles are defined by |cubicweb|:
29 +
30 +.. autodocfunction:: cubicweb.ext.rest.eid_reference_role
31 +.. autodocfunction:: cubicweb.ext.rest.rql_role
32 +.. autodocfunction:: cubicweb.ext.rest.bookmark_role
diff --git a/ext/rest.py b/ext/rest.py
@@ -34,10 +34,11 @@
33 
34  from cStringIO import StringIO
35  from itertools import chain
36  from logging import getLogger
37  from os.path import join
38 +from urlparse import urlsplit
39 
40  from docutils import statemachine, nodes, utils, io
41  from docutils.core import Publisher
42  from docutils.parsers.rst import Parser, states, directives
43  from docutils.parsers.rst.roles import register_canonical_role, set_classes
@@ -126,10 +127,67 @@
44      except Exception as exc:
45          content = 'an error occured while interpreting this rql directive: %r' % exc
46      set_classes(options)
47      return [nodes.raw('', content, format='html')], []
48 
49 +def bookmark_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
50 +    """:bookmark:`<bookmark-eid>` or :bookmark:`<eid>:<vid>`
51 +
52 +    Example: :bookmark:`1234:table`
53 +
54 +    Replace the directive with the output of applying the view to the resultset
55 +    returned by the query stored in the bookmark. By default, the view is the one
56 +    stored in the bookmark, but it can be overridden by the directive as in the
57 +    example above.
58 +
59 +    "X eid %(userid)s" can be used in the RQL query stored in the Bookmark, for
60 +    this query will be executed with the argument {'userid': _cw.user.eid}.
61 +    """
62 +    _cw = inliner.document.settings.context._cw
63 +    text = text.strip()
64 +    try:
65 +        if ':' in text:
66 +            eid, vid = text.rsplit(u':', 1)
67 +            eid = int(eid)
68 +        else:
69 +            eid, vid = int(text), None
70 +    except ValueError:
71 +        msg = inliner.reporter.error(
72 +            'EID number must be a positive number; "%s" is invalid.'
73 +            % text, line=lineno)
74 +        prb = inliner.problematic(rawtext, rawtext, msg)
75 +        return [prb], [msg]
76 +    try:
77 +        bookmark = _cw.entity_from_eid(eid)
78 +    except UnknownEid:
79 +        msg = inliner.reporter.error('Unknown EID %s.' % text, line=lineno)
80 +        prb = inliner.problematic(rawtext, rawtext, msg)
81 +        return [prb], [msg]
82 +    try:
83 +        params = dict(_cw.url_parse_qsl(urlsplit(bookmark.path).query))
84 +        rql = params['rql']
85 +        if vid is None:
86 +            vid = params.get('vid')
87 +    except (ValueError, KeyError), exc:
88 +        msg = inliner.reporter.error('Could not parse bookmark path %s [%s].'
89 +                                     % (bookmark.path, exc), line=lineno)
90 +        prb = inliner.problematic(rawtext, rawtext, msg)
91 +        return [prb], [msg]
92 +    try:
93 +        rset = _cw.execute(rql, {'userid': _cw.user.eid})
94 +        if rset:
95 +            if vid is None:
96 +                vid = vid_from_rset(_cw, rset, _cw.vreg.schema)
97 +        else:
98 +            vid = 'noresult'
99 +        view = _cw.vreg['views'].select(vid, _cw, rset=rset)
100 +        content = view.render()
101 +    except Exception, exc:
102 +        content = 'An error occured while interpreting directive bookmark: %r' % exc
103 +    set_classes(options)
104 +    return [nodes.raw('', content, format='html')], []
105 +
106  def winclude_directive(name, arguments, options, content, lineno,
107                         content_offset, block_text, state, state_machine):
108      """Include a reST file as part of the content of this reST file.
109 
110      same as standard include directive but using config.locate_doc_resource to
@@ -321,8 +379,9 @@
111      if _INITIALIZED:
112          return
113      _INITIALIZED = True
114      register_canonical_role('eid', eid_reference_role)
115      register_canonical_role('rql', rql_role)
116 +    register_canonical_role('bookmark', bookmark_role)
117      directives.register_directive('winclude', winclude_directive)
118      if pygments_directive is not None:
119          directives.register_directive('sourcecode', pygments_directive)
diff --git a/ext/test/unittest_rest.py b/ext/test/unittest_rest.py
@@ -73,7 +73,14 @@
120      def test_rql_role_without_vid(self):
121          context = self.context()
122          out = rest_publish(context, ':rql:`Any X WHERE X is CWUser`')
123          self.assertEqual(out, u'<p><h1>CWUser_plural</h1><div class="section"><a href="http://testing.fr/cubicweb/cwuser/admin" title="">admin</a></div><div class="section"><a href="http://testing.fr/cubicweb/cwuser/anon" title="">anon</a></div></p>\n')
124 
125 +    def test_bookmark_role(self):
126 +        context = self.context()
127 +        rset = self.execute('INSERT Bookmark X: X title "hello", X path "/view?rql=Any X WHERE X is CWUser"')
128 +        eid = rset[0][0]
129 +        out = rest_publish(context, ':bookmark:`%s`' % eid)
130 +        self.assertEqual(out, u'<p><h1>CWUser_plural</h1><div class="section"><a href="http://testing.fr/cubicweb/cwuser/admin" title="">admin</a></div><div class="section"><a href="http://testing.fr/cubicweb/cwuser/anon" title="">anon</a></div></p>\n')
131 +
132  if __name__ == '__main__':
133      unittest_main()