Alexander Afanasyev | 7c10b3b | 2015-01-20 12:24:27 -0800 | [diff] [blame] | 1 | # -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- |
| 2 | # Based on http://doughellmann.com/2010/05/09/defining-custom-roles-in-sphinx.html |
| 3 | |
| 4 | """Integration of Sphinx with Redmine. |
| 5 | """ |
| 6 | |
| 7 | from docutils import nodes, utils |
| 8 | from docutils.parsers.rst.roles import set_classes |
| 9 | |
| 10 | def redmine_role(name, rawtext, text, lineno, inliner, options={}, content=[]): |
| 11 | """Link to a Redmine issue. |
| 12 | |
| 13 | Returns 2 part tuple containing list of nodes to insert into the |
| 14 | document and a list of system messages. Both are allowed to be |
| 15 | empty. |
| 16 | |
| 17 | :param name: The role name used in the document. |
| 18 | :param rawtext: The entire markup snippet, with role. |
| 19 | :param text: The text marked with the role. |
| 20 | :param lineno: The line number where rawtext appears in the input. |
| 21 | :param inliner: The inliner instance that called us. |
| 22 | :param options: Directive options for customization. |
| 23 | :param content: The directive content for customization. |
| 24 | """ |
| 25 | try: |
| 26 | issue_num = int(text) |
| 27 | if issue_num <= 0: |
| 28 | raise ValueError |
| 29 | except ValueError: |
| 30 | msg = inliner.reporter.error( |
| 31 | 'Redmine issue number must be a number greater than or equal to 1; ' |
| 32 | '"%s" is invalid.' % text, line=lineno) |
| 33 | prb = inliner.problematic(rawtext, rawtext, msg) |
| 34 | return [prb], [msg] |
| 35 | app = inliner.document.settings.env.app |
| 36 | node = make_link_node(rawtext, app, 'issues', str(issue_num), options) |
| 37 | return [node], [] |
| 38 | |
| 39 | def make_link_node(rawtext, app, type, slug, options): |
| 40 | """Create a link to a Redmine resource. |
| 41 | |
| 42 | :param rawtext: Text being replaced with link node. |
| 43 | :param app: Sphinx application context |
| 44 | :param type: Link type (issue, changeset, etc.) |
| 45 | :param slug: ID of the thing to link to |
| 46 | :param options: Options dictionary passed to role func. |
| 47 | """ |
| 48 | # |
| 49 | try: |
| 50 | base = app.config.redmine_project_url |
| 51 | if not base: |
| 52 | raise AttributeError |
| 53 | except AttributeError, err: |
| 54 | raise ValueError('redmine_project_url configuration value is not set (%s)' % str(err)) |
| 55 | # |
| 56 | slash = '/' if base[-1] != '/' else '' |
| 57 | ref = base + slash + type + '/' + slug + '/' |
| 58 | set_classes(options) |
| 59 | node = nodes.reference(rawtext, 'Issue #' + utils.unescape(slug), refuri=ref, |
| 60 | **options) |
| 61 | return node |
| 62 | |
| 63 | def setup(app): |
| 64 | """Install the plugin. |
| 65 | |
| 66 | :param app: Sphinx application context. |
| 67 | """ |
| 68 | app.add_role('issue', redmine_role) |
| 69 | app.add_config_value('redmine_project_url', None, 'env') |
| 70 | return |