source: mermaidmacro/1.0/tracmermaid/mermaid.py

Last change on this file was 17355, checked in by Ryan J Ollos, 4 years ago

Fix Error: int object has no attribute replace

Fixes #13546.

File size: 5.7 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2016 tkob <ether4@gmail.com>
4# All rights reserved.
5#
6# This software is licensed as described in the file COPYING, which
7# you should have received as part of this distribution.
8
9import re
10import uuid
11
12from trac.core import implements
13from trac.util.text import unicode_quote
14from trac.util.html import Fragment, Element, escape
15from trac.web.chrome import (add_script, add_script_data, add_stylesheet,
16                             Chrome, ITemplateProvider)
17from trac.web.main import IRequestHandler
18from trac.wiki.api import IWikiPageManipulator
19from trac.wiki.formatter import extract_link
20from trac.wiki.macros import WikiMacroBase
21from trac.wiki.model import WikiPage
22
23
24class MermaidMacro(WikiMacroBase):
25    implements(ITemplateProvider, IRequestHandler, IWikiPageManipulator)
26
27    def expand_macro(self, formatter, name, content, args=None):
28        self.log.debug("content %s", content)
29        context = formatter.context
30        req = formatter.req
31        if args is None or 'id' not in args:
32            id_attr = ''
33        else:
34            id_attr = 'id="%s"' % escape(args['id'])
35        if not req:
36            # Off-line rendering (there's a command line API for mermaid)
37            return '<img alt="not-yet-implemented"/>'
38        Chrome(self.env).add_jquery_ui(req)
39        add_stylesheet(req, 'mermaid/mermaid.css')
40        add_script(req, 'mermaid/mermaid.min.js')
41        add_script(req, 'mermaid/tracmermaid.js')
42        add_script_data(req, {
43            '_tracmermaid': {
44                'submit': req.href + '/mermaid/submit',
45            },
46            'form_token': req.form_token,
47        })
48        content = self.expand_links(context, content)
49        return """\
50            <div class="mermaid"
51                       %s
52                       data-mermaidresourcerealm="%s"
53                       data-mermaidresourceid="%s"
54                       data-mermaidresourceversion="%s"
55                       data-mermaidsource="%s">%s
56            </div>
57            <script type="text/javascript">
58                if (typeof mermaid !== 'undefined') {
59                    mermaid.init(); // ok to call repeatedly (data-processed)
60                    $(".mermaid g[title]").css('cursor', 'pointer');
61                }
62            </script>""" % (
63                id_attr,
64                escape(context.resource.realm),
65                escape(unicode(context.resource.id)),
66                escape(unicode(context.resource.version or '')),
67                escape(unicode_quote(content)),
68                escape(content))
69
70    click_re = re.compile(r'^\s*click\s+\w+\s+(trac)\s+(.*)$')
71
72    def expand_links(self, context, content):
73        lines = []
74        for line in content.splitlines():
75            # "Native" mermaid link (left alone):
76            #   click A callback "This is a tooltip for a link"
77            #   click B "http://www.github.com" "This is a tooltip for a link"
78            #
79            # TracLinks link (transformed to native):
80            #   click A trac TracLinks
81            #   click A trac r123
82            #   click A trac [123]
83            #   click A trac [[TracLinks|Anything that can be parsed as a link]]
84            m = self.click_re.match(line)
85            if m:
86                link = m.group(2)
87                link = extract_link(self.env, context, link)
88                if isinstance(link, Element):
89                    href = link.attrib.get('href')
90                    title = link.attrib.get('title', '')
91                    for c in link.children:
92                        if not isinstance(c, Fragment):
93                            name = c
94                            break
95                    else:
96                        name = title
97                    line = line[0:m.start(1)] + '"%s" "%s"' % (
98                        href.replace('"', ''), name.replace('"', ''))
99            lines.append(line)
100        return '\n'.join(lines)
101
102    # ITemplateProvider methods
103
104    def get_htdocs_dirs(self):
105        from pkg_resources import resource_filename
106        return [('mermaid', resource_filename(__name__, 'htdocs'))]
107
108    def get_templates_dirs(self):
109        return []
110
111    # IRequestHandler methods
112
113    def match_request(self, req):
114        self.log.debug('match_request: path_info=' + req.path_info)
115        return req.path_info.startswith('/mermaid/submit')
116
117    def process_request(self, req):
118        self.log.debug('process_request: req=' + str(req))
119        self.log.debug('process_request: args=' + str(req.args))
120
121        id = req.args['id']
122        page_name = req.args['wikipage']
123        source = req.args['source']
124        page = WikiPage(env=self.env, name=page_name)
125        lines = page.text.splitlines()
126        lines.reverse()
127        buf = []
128        while len(lines) > 0:
129            line = lines.pop()
130            if line == ('{{{#!Mermaid id="%s"' % id):
131                buf.append(line)
132                buf.append(source)
133                while len(lines) > 0:
134                    line = lines.pop()
135                    if line.rstrip() == "}}}":
136                        buf.append(line)
137                        break
138            else:
139                buf.append(line)
140        page.text = "\n".join(buf)
141
142        req.perm(page).require('WIKI_MODIFY')
143        comment = "Update from Mermaid live editor"
144        page.save(req.authname, comment, req.remote_addr)
145
146        req.send("OK", 'text/plain')
147
148    # IWikiPageManipulator methods
149
150    def prepare_wiki_page(self, req, page, fields):
151        pass
152
153    def validate_wiki_page(self, req, page):
154        buf = []
155        for line in page.text.splitlines():
156            if line == "{{{#!Mermaid":
157                buf.append('{{{#!Mermaid id="%s"' % str(uuid.uuid1()))
158            else:
159                buf.append(line)
160        page.text = "\n".join(buf)
161        return []
Note: See TracBrowser for help on using the repository browser.