Beispiel #1
0
 def expand_macro(self, formatter, name, content):
     
     if not content:
         return ''
     
     args, kwargs = parse_args(content)        
     if len(args) > 1:
         return system_message("Number of args can't be greater than 1")
     
     if args[0] == 'author':
         page = WikiPage(self.env, formatter.context.resource, 1)
         text = page.author 
     elif args[0] == 'version':
         page = WikiPage(self.env, formatter.context.resource)
         text = str(page.version)
     elif args[0] == 'changed_by':
         page = WikiPage(self.env, formatter.context.resource)
         text = page.author
     elif args[0] == 'comment':
         page = WikiPage(self.env, formatter.context.resource)
         text = page.comment
     elif args[0] == 'changed_ts':
         page = WikiPage(self.env, formatter.context.resource)
         text = format_datetime(page.time)
     else:
         return system_message("Unkwown argument %s" % args[0])
     
     return format_to_oneliner(self.env, formatter.context, text)
Beispiel #2
0
    def expand_macro(self, formatter, name, content):
        args, kw = parse_args(content)
        utc_dt = args and args[0] or None
        if not utc_dt:
            return system_message('IrcLogQuote: Timestamp required')
        d = self.date_re.match(utc_dt)
        if not d:
            return system_message('IrcLogQuote: Invalid timestamp format')
        offset = int(args and len(args)>1 and args[1] or 10)

        irclogs =  IrcLogsView(self.env)        
        logfile = irclogs._get_filename(d.group('year'),
                                        d.group('month'),
                                        d.group('day'))
        if (not os.path.isfile(logfile)):
            return system_message('IrcLogQuote: No log file for this date')
        iterable = irclogs._to_unicode(file(logfile))
        lines = irclogs._render_lines(iterable, formatter.req.tz)

        filtered_lines = [line for line in lines 
                                if unicode(line['utc_dt']) >= utc_dt
                                    and line['mode'] == 'channel'
                                    and line['hidden_user'] != 'hidden_user']
        
        add_stylesheet(formatter.req, 'irclogs/style.css')
        data = Chrome(self.env).populate_data(formatter.req, 
                                    {'lines':filtered_lines[:offset],
                                     'excerpt_date':d.groupdict(),
                                     'utc_dt':utc_dt})
        return Chrome(self.env).load_template('macro_quote.html') \
                                    .generate(**data)
Beispiel #3
0
 def expand_macro(self, formatter, name, content):
     req = formatter.req
     if not content:
         return system_message("A title must be provided as the first argument to the poll macro")
     content = filter(None, [i.strip() for i in
                             content.replace(';', '\n').split('\n')])
     if len(content) < 2:
         return system_message("One or more options must be provided to vote on.")
     title = content.pop(0)
     return self.render_poll(req, title, content)
Beispiel #4
0
    def expand_macro(self, formatter, name, content):
        req = formatter.req   # Shortcut.
        safe_content = False  # Whether or not to disable cleaning HTML.
        args = [x.strip() for x in content.split(',')]
        if len(args) == 1:
            args.append(None)
        elif len(args) == 3:
            return system_message('args == %s' % args)
            if not args[2].startswith('fragment='):
                msg = ('If three arguments are given, the last one must'
                       ' start with fragment=, but tag content was %s')
                return system_message(msg % content)
        elif len(args) != 2:
            return system_message('Invalid arguments "%s"'%content)

        # Parse out fragment name.
        fragment_name = None
        if args[-1] and args[-1].startswith('fragment='):
            fragment_name = args[-1][len('fragment='):]
            args.pop()
        if len(args) == 1:
            args.append(None)
            
        # Pull out the arguments
        source, dest_format = args
        try:
            source_format, source_obj = source.split(':', 1)
        except ValueError: # If no : is present, assume its a wiki page
            source_format, source_obj = 'wiki', source
            
        # Apply a default format if needed
        if dest_format is None:
            try:
                dest_format = self.default_formats[source_format]
            except KeyError:
                pass
        
        if source_format in ('http', 'https', 'ftp'):
            # Since I can't really do recursion checking, and because this 
            # could be a source of abuse allow selectively blocking it.
            # RFE: Allow blacklist/whitelist patterns for URLS. <NPK>
            # RFE: Track page edits and prevent unauthorized users from ever entering a URL include. <NPK>
            if not req.perm.has_permission('INCLUDE_URL'):
                self.log.info('IncludeMacro: Blocking attempt by %s to include URL %s on page %s', req.authname, source, req.path_info)
                return ''
            try:
                urlf = urllib2.urlopen(source)
                out = urlf.read()  
            except urllib2.URLError, e:
                return system_message('Error while retrieving file', str(e))
            except TracError, e:
                return system_message('Error while previewing', str(e))
Beispiel #5
0
    def expand_macro(self, formatter, name, args):
        """Return value of a trac.ini [section].option as plain text."""
        
        if args.find('.') == -1:
            return system_message('%s: Invalid parameter: "%s"' % (name, args))
        section, option = re.sub('\s+', '', args).split('.', 1)

        if self.config.has_option(section, option):
            return (self.config.get(section, option))
        else:
            return system_message('%s: No option "%s" in section [%s]' % (name,
                                                                          option,
                                                                          section))
Beispiel #6
0
    def expand_macro(self, formatter, macro, args):

        args, kw = parse_args(args)

        try:
            source = args.pop(0).strip()
        except NameError:
            return system_message('%s: Missing HTML source argument.' % macro)

        try:
            stream = Stream(HTMLParser(StringIO(source)))
            return (stream | TracHTMLSanitizer()).render('xhtml', encoding=None)
        except ParseError, e:
            self.env.log.warn(e)
            return system_message('%s: HTML parse error: %s.' % (macro, escape(e.msg)))
Beispiel #7
0
 def expand_macro(self, formatter, name, content):
     db = self.env.get_db_cnx()
     cursor = db.cursor()
     try:
         cursor.execute(content)
     except Exception, e:
         return system_message(_("Invalid SQL"), exception_to_unicode(e))
Beispiel #8
0
    def expand_macro(self, formatter, macro, args):

        # dictionary file

        section = macro.lower() # [abbr]
        option = 'file'         # file = /path/to/dictionary/file
        dict = {}

        if self.config.has_option(section, option):
            dict = build_dict(self.config.get(section, option), self.tags)

        args, kw = parse_args(args)

        try:
            for key in self.keys:
                kw[key] = kw[key].strip()
        except KeyError:
            if key == 'title' and kw['key'] in dict:
                kw['title'] = dict[kw['key']][0]
                kw['tag'] = dict[kw['key']][1]
            else:
                return system_message('%s: Missing required keyword "%s."' %
                                     (macro, key))
        # tag keyword is optional

        if 'tag' in kw:
            tag = kw['tag'].strip()
            if tag not in self.tags: tag = self.default
        else:
            tag = self.default

        return Element(tag)(kw['key'], title="%s" % kw['title'])
Beispiel #9
0
    def expand_macro(self, formatter, name, content):

        env = formatter.env
        abs = env.abs_href.base
        abs = abs[:len(abs) - len(env.href.base)]
        f = Formatter(formatter.env, formatter.context)
        
        def convert(m):
            pre, target, suf = filter(None, m.groups())
            out = StringIO()
            f.format(target, out)
            url = search(
                    HREF,
                    out.getvalue(),
                    I).groups()[0]
            # Trac creates relative links, which Markdown won't touch inside
            # <autolinks> because they look like HTML
            if pre == '<' and url != target:
               pre += abs
            return pre + str(url) + suf
            
        try:
            from markdown import markdown
            return markdown(sub(LINK, convert, content))
        except ImportError:
            msg = 'Error importing Python Markdown, install it from '
            url = 'http://www.freewisdom.org/projects/python-markdown/'
            return system_message(tag(msg, tag.a('here', href="%s" % url), '.'))
Beispiel #10
0
 def get_macro_descr():
     for macro_provider in formatter.wiki.macro_providers:
         names = list(macro_provider.get_macros() or [])
         if name_filter and not any(name.startswith(name_filter)
                                    for name in names):
             continue
         try:
             name_descriptions = [
                 (name, macro_provider.get_macro_description(name))
                 for name in names]
         except Exception, e:
             yield system_message(
                 _("Error: Can't get description for macro %(name)s",
                   name=names[0]), e), names
         else:
             for descr, pairs in groupby(name_descriptions,
                                         key=lambda p: p[1]):
                 if descr:
                     if isinstance(descr, (tuple, list)):
                         descr = dgettext(descr[0],
                                          to_unicode(descr[1])) \
                                 if descr[1] else ''
                     else:
                         descr = to_unicode(descr) or ''
                     if content == '*':
                         descr = format_to_oneliner(
                             self.env, formatter.context, descr,
                             shorten=True)
                     else:
                         descr = format_to_html(
                             self.env, formatter.context, descr)
                 yield descr, [name for name, descr in pairs]
Beispiel #11
0
    def expand_macro(self, formatter, name, text, args):
        """Execute the macro
        """
        self.env.log.debug("TestPlanMacro.expand_macro()")

        parser = TestcaseParser(self.env)

        self.env.log.debug( "name: %s", str(name))
        self.env.log.debug( "args: %s", str(args))
        self.env.log.debug( "text: %s", str(text))

        errors = list()
        # Parse config and testcases
        attrs, tcnames_and_users = TestPlanMacroParser(self.env).parse_config(text)
        self.log.debug("attrs: %s" % attrs)

        # syntax check the testcases
        for tcname, users in tcnames_and_users:
            try:
                parser.parseTestcase(tcname)
            except TracError, e:
                # FIXME: commented because auf genshi unicode error - raised
                # instead, see other FIXME below!
                #"""
                error_message = safe_unicode("Parsing error in Testcase %s:" %
                        tcname)
                errors.append(system_message(error_message,
                    text= safe_unicode(e.message)))
    def expand_macro(self, formatter, name, content):
        # Example: [[ASA(42)]]
        args, kw = parse_args(content)
        args = [arg.strip() for arg in args]
        if not args or not args[0].isdigit():
            raise TracError('Custom artifact id not specified')
        args, kw = parse_args(content)
        if not args or not args[0].isdigit():
            raise TracError('Custom artifact id not specified')
        artifact_id = int(args[0])
        dbp = DBPool(self.env, InstancePool())
        try:
            dbp.load_artifact(id=artifact_id)
        except ValueError:
            return system_message("Custom Artifact not found", "No custom artifact was found for id '{0}'.".format(artifact_id))
        artifact = dbp.pool.get_item(id=artifact_id)
        artifact_url = formatter.req.href.customartifacts('artifact/{0}'.format(artifact.get_id()))
        res = Core._get_resource(artifact) if not artifact in (Entity, Instance, None) and not type(artifact)==unicode else None
        spec_name, spec_url, values = _get_artifact_details(artifact, formatter.req)

        tpl='view_artifact_dialog.html'
        data = {
            'context': Context.from_request(formatter.req, res),
            'spec_name': spec_name,
            'spec_url': spec_url,
            'artifact': artifact,
            'artifact_url': artifact_url,
            'artifacts_values': values,
        }
        return Chrome(self.env).render_template(formatter.req, tpl, data, None, fragment=True)
Beispiel #13
0
    def expand_macro(self, formatter, name, content, args = None):
        if args is None:
            args = {}

        if name[-4:] in ('_svg', '_png'):
            name, type = name.split('_')
        else:
            type = (args.get('type') or self._default_type).lower()
            if type not in ('svg', 'png'):
                return system_message("Invalid type(%s). Type must be 'svg' or 'png'" % type)
                
        font = self._font

        # nonascii unicode can't be passed to hashlib.
        id = make_hash('%s,%s,%s,%r' % (name, type, font, content)).hexdigest()

        ## Create img tag.
        params = { "src": formatter.req.href("%s/%s.%s" % (name, id, type)) }
        for key, value in args.iteritems():
            if key != "type":
                params[key] = value
        output = tag.img(**params)

        ## Generate image and cache it.
        def generate_image():
            infile = mktemp(prefix='%s-' % name)
            outfile = mktemp(prefix='%s-' % name)
            try:
                try:
                    f = codecs.open(infile, 'w', 'utf8')
                    try:
                        f.write(content)
                    finally:
                        f.close()
                    cmd = [name, '-a', '-T', type, '-o', outfile, infile]
                    if font:
                        cmd.extend(['-f', font])
                    self.env.log.debug('(%s) command: %r' % (name, cmd))
                    try:
                        proc = Popen(cmd, stderr=PIPE)
                        stderr_value = proc.communicate()[1]
                    except Exception, e:
                        self.env.log.error('(%s) %r' % (name, e))
                        raise ImageGenerationError("Failed to generate diagram. (%s is not found.)" % name)
    
                    if proc.returncode != 0 or not os.path.isfile(outfile):
                        self.env.log.error('(%s) %s' % (name, stderr_value))
                        raise ImageGenerationError("Failed to generate diagram. (rc=%d)" % proc.returncode)
                    f = open(outfile, 'rb')
                    try:
                        data = f.read()
                    finally:
                        f.close()
                    return data
                except ImageGenerationError:
                    raise
                except Exception, e:
                    self.env.log.error('(%s) %r' % (name, e))
                    raise ImageGenerationError("Failed to generate diagram.")
Beispiel #14
0
 def expand_macro(self, formatter, name, text, args):
     """Execute the macro
     """
     parser = TestcaseParser(self.env)
     out = StringIO.StringIO()
     f = Formatter(self.env, formatter.context)
     try:
         parser.parseTestcase(text=text)
     except Exception, e:
         out.write(system_message("Parsing error", text=e))
Beispiel #15
0
 def get_macro_descr():
     for macro_provider in wiki.macro_providers:
         for macro_name in macro_provider.get_macros():
             if content and macro_name != content:
                 continue
             try:
                 descr = macro_provider.get_macro_description(macro_name)
                 descr = wiki_to_html(descr or "", self.env, req)
             except Exception, e:
                 descr = Markup(system_message("Error: Can't get description for macro %s" % macro_name, e))
             yield (macro_name, descr)
Beispiel #16
0
    def expand_macro(self, formatter, name, content):
        _, kw = parse_args(content)
        channel_name = kw.get('channel')
        utc_dt = kw.get('datetime')
        if not utc_dt:
            return system_message('IrcLogQuote: arguments required '\
                '(channel=channel, datetime=timestamp(UTCYYYY-MM-DDTHH:MM:SS), '\
                'offset=seonds)')
        d = self.date_re.match(utc_dt.strip())
        if not d:
            return system_message('IrcLogQuote: Invalid timestamp format')
        offset = int(kw.get('offset', 10))

        irclogs = IrcLogsView(self.env)        
        ch_mgr = IRCChannelManager(self.env)
        start = datetime(*strptime(utc_dt, self.date_format)[:6])
        start = UTC.localize(start)
        start = ch_mgr.to_user_tz(formatter.req, start)
        end = start + timedelta(seconds=offset)
        channel = ch_mgr.channel(channel_name)
        formatter.req.perm.assert_permission(channel.perm())
        lines = channel.events_in_range(start, end)
        lines = filter(lambda x: not x.get('hidden'), 
                map(irclogs._map_lines, lines))
        rows = map(irclogs._render_line, lines)

        add_stylesheet(formatter.req, 'irclogs/css/irclogs.css')
        data = Chrome(self.env).populate_data(
            formatter.req, 
            {
                'channel': channel_name,
                'lines': lines,
                'year': '%04d'%(start.year),
                'month': '%02d'%(start.month),
                'day': '%02d'%(start.day),
                'time': start.strftime("%H:%M:%S"),
                'rows': rows
            }
        )
        return Chrome(self.env).load_template('macro_quote.html') \
                                    .generate(**data)
Beispiel #17
0
    def expand_macro(self, formatter, name, content):
        args = [x.strip() for x in content.split(",")]
        if len(args) == 1:
            args.append(None)
        elif len(args) != 2:
            return system_message('Invalid arguments "%s"' % content)

        # Pull out the arguments
        source, dest_format = args
        try:
            source_format, source_obj = source.split(":", 1)
        except ValueError:  # If no : is present, assume its a wiki page
            source_format, source_obj = "wiki", source

        # Apply a default format if needed
        if dest_format is None:
            try:
                dest_format = self.default_formats[source_format]
            except KeyError:
                pass

        if source_format in ("http", "https", "ftp"):
            # Since I can't really do recursion checking, and because this
            # could be a source of abuse allow selectively blocking it.
            # RFE: Allow blacklist/whitelist patterns for URLS. <NPK>
            # RFE: Track page edits and prevent unauthorized users from ever entering a URL include. <NPK>
            if not formatter.perm.has_permission("INCLUDE_URL"):
                self.log.info(
                    "IncludeMacro: Blocking attempt by %s to include URL %s on page %s",
                    formatter.req.authname,
                    source,
                    formatter.req.path_info,
                )
                return ""
            try:
                urlf = urllib2.urlopen(source)
                out = urlf.read()
            except urllib2.URLError, e:
                return system_message("Error while retrieving file", str(e))
            except TracError, e:
                return system_message("Error while previewing", str(e))
Beispiel #18
0
 def _render_page_outline(self, formatter, ol, page_resource, active, params):
     page = params.get('root', '') + page_resource.id
     page_text, page_exists = self.get_page_text(formatter, page_resource)
     if page_exists:
         ctx = formatter.context(page_resource)
         fmt = OutlineFormatter(self.env, ctx)
         fmt.format(page_text, NullOut())
         outline_tree(self.env, ol, fmt.outline, ctx,
                 active and page_resource.id == formatter.context.resource.id,
                 params['min_depth'], params['max_depth'])
     else:
         ol.append(system_message('Error: Page %s does not exist' %
                                  page_resource.id))
Beispiel #19
0
 def get_macro_descr():
     for macro_provider in formatter.wiki.macro_providers:
         for macro_name in macro_provider.get_macros():
             if content and macro_name != content:
                 continue
             try:
                 descr = macro_provider.get_macro_description(macro_name)
                 descr = format_to_html(self.env, formatter.context,
                                        to_unicode(descr) or '')
             except Exception, e:
                 descr = system_message(_("Error: Can't get description "
                                          "for macro %(name)s",
                                          name=macro_name), e)
             yield (macro_name, descr)
Beispiel #20
0
    def expand_macro(self, formatter, name, args):
        if args is None:
            return system_message("No UML text defined!")
        if not self.plantuml_jar:
            return system_message("plantuml_jar option not defined in .ini")
        if not os.path.exists(self.plantuml_jar):
            return system_message("plantuml.jar not found: %s" % self.plantuml_jar)

        source = args.encode('utf-8').strip()

        try:
            graphs = pickle.loads(base64.b64decode(formatter.req.session.get('plantuml',"")))
        except:
            graphs = {}
            
        img_id = hashlib.sha1(source).hexdigest()            
            
        #Clean old images
        for key, graph in graphs.items():
            if (datetime.now() - graph[1]).seconds > 3600:
                del graphs[key]              

        if not graphs.has_key(img_id):
        
            cmd = "java -jar -Djava.awt.headless=true \"%s\" -charset UTF-8 -pipe" % (self.plantuml_jar)
            p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
            (stdout, stderr) = p.communicate(input=source)
            if p.returncode != 0:
                return system_message("Error running plantuml: %s" % stderr)

            graphs[img_id] = (stdout, datetime.now())        
        
        
        formatter.req.session['plantuml'] = base64.b64encode(pickle.dumps(graphs))

        out = "{{{\n#!html\n<img src='%s' alt='PlantUML Diagram' />\n}}}\n" % formatter.href("plantuml", id=img_id)
        return wiki_to_html(out, self.env, formatter.req)
Beispiel #21
0
    def process_request(self, req):
        req.perm.assert_permission('DOXYGEN_VIEW')

        # Get request arguments
        path = req.args.get('path')
        action = req.args.get('action')

        # Get config variables
        index =  self.config.get('doxygen', 'index', 'main.html')
        wiki_index = self.config.get('doxygen', 'wiki_index', None)

        # Redirect search requests.
        if action == 'search':
            req.redirect('%s?q=%s&doxygen=on' % (self.env.href.search(),
              req.args.get('query')))

        # Retrun apropriate content to type or search request
        elif action == 'index':
            if wiki_index:
                # Get access to database
                db = self.env.get_db_cnx()
                cursor = db.cursor()

                # Get wiki index
                sql = "SELECT text FROM wiki WHERE name = %s"
                cursor.execute(sql, (wiki_index,))
                text = Markup(system_message('Error', 'Wiki page %s does not' \
                  ' exists' % (wiki_index)))
                for row in cursor:
                    text = wiki_to_html(row[0], self.env, req)

                # Display wiki index page
                req.hdf['doxygen.text'] = text
                return 'doxygen.cs', 'text/html'
            else:
                add_stylesheet(req, 'doxygen/css/doxygen.css')
                req.hdf['doxygen.path'] = path + '/' + index
                return 'doxygen.cs', 'text/html'

        elif action == 'file':
            type = mimetypes.guess_type(path)[0]

            if type == 'text/html':
                add_stylesheet(req, 'doxygen/css/doxygen.css')
                req.hdf['doxygen.path'] = path
                return 'doxygen.cs', 'text/html'
            else:
                req.send_file(path, type)
Beispiel #22
0
    def expand_macro(self, formatter, name, text, args):
        if not text:
            raw_actions = self.config.options('ticket-workflow')
        else:
            if args is None:
                text = '\n'.join([line.lstrip() for line in text.split(';')])
            if '[ticket-workflow]' not in text:
                text = '[ticket-workflow]\n' + text
            parser = RawConfigParser()
            try:
                parser.readfp(StringIO(text))
            except ParsingError as e:
                return system_message(_("Error parsing workflow."),
                                      unicode(e))
            raw_actions = list(parser.items('ticket-workflow'))
        actions = parse_workflow_config(raw_actions)
        states = list(set(
            [state for action in actions.itervalues()
                   for state in action['oldstates']] +
            [action['newstate'] for action in actions.itervalues()]))
        action_labels = [attrs['label'] for attrs in actions.values()]
        action_names = actions.keys()
        edges = []
        for name, action in actions.items():
            new_index = states.index(action['newstate'])
            name_index = action_names.index(name)
            for old_state in action['oldstates']:
                old_index = states.index(old_state)
                edges.append((old_index, new_index, name_index))

        args = args or {}
        width = args.get('width', 800)
        height = args.get('height', 600)
        graph = {'nodes': states, 'actions': action_labels, 'edges': edges,
                 'width': width, 'height': height}
        graph_id = '%012x' % id(graph)
        req = formatter.req
        add_script(req, 'common/js/excanvas.js', ie_if='IE')
        add_script(req, 'common/js/workflow_graph.js')
        add_script_data(req, {'graph_%s' % graph_id: graph})
        return tag(
            tag.div('', class_='trac-workflow-graph trac-noscript',
                    id='trac-workflow-graph-%s' % graph_id,
                    style="display:inline-block;width:%spx;height:%spx" %
                          (width, height)),
            tag.noscript(
                tag.div(_("Enable JavaScript to display the workflow graph."),
                        class_='system-message')))
Beispiel #23
0
 def _render_title_index(self, formatter, ol, page_resource, active, show_title):
     page_text, page_exists = self.get_page_text(formatter, page_resource)
     if not page_exists:
         ol.append(system_message('Error: No page matching %s found' %
                                  page_resource.id))
         return
     ctx = formatter.context(page_resource)
     fmt = OutlineFormatter(self.env, ctx)
     fmt.format(page_text, NullOut())
     title = ''
     if show_title and fmt.outline:
         title = ': ' + fmt.outline[0][2]
     ol.append((tag.li(tag.a(page_resource.id,
                   href=get_resource_url(self.env, page_resource, ctx.href)),
                   Markup(title),
                   class_= active and 'active' or None)))
    def expand_macro(self, formatter, name, content):

        env = formatter.env
        abs = env.abs_href.base
        abs = abs[:len(abs) - len(env.href.base)]
        f = Formatter(formatter.env, formatter.context)
        
        def convert_links(m):
            pre, target, suf = filter(None, m.groups())
            out = StringIO()
            f.format(target, out)
            url = re.search(HREF, out.getvalue()).groups()[0]
            # Trac creates relative links, which Markdown won't touch inside
            # <autolinks> because they look like HTML
            if pre == '<' and url != target:
                pre += abs
            return pre + str(url) + suf
        
	def emojify(html):
            pattern = ":([a-z0-9\\+\\-_]+):"
            link = "<img\
                alt=\"\\1\"\
                title=\":\\1:\"\
                height=\"20\"\
                style=\"vertical-align:middle\"\
                width=\"20\"\
                src=\"/chrome/markdown/emoji/\\1.png\" />"

            emojify_html = re.sub(pattern, link, html)
            return emojify_html

        try:
            # Import & convert
            import markdown2
            # autolink http:// n stuff
            autolinked_content = re.sub(LINK, convert_links, content)
            # convert to markdown
            html = markdown2.markdown(autolinked_content, extras=MD_EXTRAS)
            # substitute emojis
            emojified_html = emojify(html)

            return emojified_html
        except ImportError:
            # no markdown2 package found?
            msg = 'Error importing python-markdown2, install it from '
            url = 'https://github.com/trentm/python-markdown2'
            return system_message(tag(msg, tag.a('here', href="%s" % url), '.'))
Beispiel #25
0
 def _format_link(self, formatter, ns, target, label):
    m = self.date_re.match(target)
    if not m:
        return system_message('Invalid IRC Log Link: '
                  'Must be of the format channel-UTCYYYY-MM-DDTHH:MM:SS %s')
    if not m.group('datetime'):
        return html.a(label, title=label, href=formatter.href.irclogs(
            m.group('channel')))
    else:
        ch_mgr = IRCChannelManager(self.env)
        t = strptime(m.group('datetime'), self.date_format)
        dt = UTC.localize(datetime(*t[:6]))
        dt = ch_mgr.to_user_tz(formatter.req, dt)
        timestr = dt.strftime(self.time_format)
        return html.a(label, title=label, href=formatter.href.irclogs(
                  m.group('channel'), '%02d'%dt.year, '%02d'%dt.month, 
                  '%02d'%dt.day,) + '#%s'%timestr)
Beispiel #26
0
    def expand_macro(self, formatter, name, content):
        if not self.plantuml_jar:
            return system_message(_("Installation error: plantuml_jar option not defined in trac.ini"))
        if not os.path.exists(self.plantuml_jar):
            return system_message(_("Installation error: plantuml.jar not found at '%s'") % self.plantuml_jar)
        
        # Trac 0.12 supports expand_macro(self, formatter, name, content, args)
        # which allows us to readily differentiate between a WikiProcess and WikiMacro
        # call. To support Trac 0.11, some additional work is required.
        try:
            args = formatter.code_processor.args
        except AttributeError:
            args = None

        path = None
        if not 'path' in args: #Could be WikiProcessor or WikiMacro call
            if content.strip().startswith("@startuml"):
                markup = content
                path = None
            else:
                path = content
                if not path:
                    return system_message(_("Path not specified"))
        elif args: #WikiProcessor with args
            path = args.get('path')
            if not path:
                return system_message(_("Path not specified"))
        
        if path:
            markup, exists = self._read_source_from_repos(formatter, path)
            if not exists:
                return system_message(_("File not found in repository: " + path))
        else:
            if not content:
                return system_message(_("No UML text defined"))
            markup = content.encode('utf-8').strip()

        img_id = hashlib.sha1(markup).hexdigest()
        if not self._is_img_existing(img_id):
            cmd = '%s -jar -Djava.awt.headless=true "%s" -charset UTF-8 -pipe' % (self.java_bin, self.plantuml_jar)
            p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
            (img_data, stderr) = p.communicate(input=markup)
            if p.returncode != 0:
                return system_message(_("Error running plantuml: '%s'") % stderr)            
            self._write_img_to_file(img_id, img_data)
        
        link = formatter.href('plantuml', id=img_id)
        return tag.img(src=link)
Beispiel #27
0
    def expand_macro(self, formatter, name, content):

        args, kw = parse_args(content)
        poll_frequency = args and args[0] or 60
        count = args and args[1] or 10

        if not (0==len(args) or 2==len(args)):
            return system_message('Incorrect arguments: ' 
                'Must be of the format (poll frequency, lines to display)')

        add_stylesheet(formatter.req, 'irclogs/style.css')
        add_script(formatter.req, 'irclogs/jquery.timer.js')

        data = Chrome(self.env).populate_data(formatter.req, 
                                    {'poll_frequency':int(poll_frequency)*1000,
                                     'count':count})
        return Chrome(self.env).load_template('macro_live.html') \
                                    .generate(**data)
    def expand_macro(self, formatter, name, content):
        req = formatter.req
        args, options = parse_args(content, strict=False)
        if len(args) != 1:
            return system_message("TotalField macro error", "request and field_name are required.")


        # we have to add custom field to query so that field is added to
        # resulting ticket list
        field_name = args[0]
        options[field_name + "!"] = None

        tickets = execute_query(self.env, req, options)

        sum = 0.0
        for t in tickets:
            try:
                sum += float(t[field_name])
            except:
                pass

        return "%g" % round(sum, 2)
Beispiel #29
0
                    req.authname, source, req.path_info)
                return ''
            try:
                urlf = urllib2.urlopen(source)
                out = urlf.read()
            except urllib2.URLError, e:
                return system_message('Error while retrieving file', str(e))
            except TracError, e:
                return system_message('Error while previewing', str(e))
        elif source_format == 'wiki':
            # XXX: Check for recursion in page includes. <NPK>
            if not req.perm.has_permission('WIKI_VIEW'):
                return ''
            page = WikiPage(self.env, source_obj)
            if not page.exists:
                return system_message('Wiki page %s does not exist' %
                                      source_obj)
            out = page.text
        elif source_format == 'source':
            if not req.perm.has_permission('FILE_VIEW'):
                return ''
            repo = self.env.get_repository(req.authname)
            node = repo.get_node(source_obj)
            out = node.get_content().read()
            if dest_format is None:
                dest_format = node.content_type or get_mimetype(
                    source_obj, out)
        # RFE: Add ticket: and comment: sources. <NPK>
        # RFE: Add attachment: source. <NPK>
        else:
            return system_message('Unsupported include source %s' % source)
Beispiel #30
0
                except ImageGenerationError:
                    raise
                except Exception, e:
                    self.env.log.error('(%s) %r' % (name, e))
                    raise ImageGenerationError("Failed to generate diagram.")
            finally:
                for path in (infile, outfile):
                    try:
                        os.remove(path)
                    except:
                        pass

        try:
            self.cache.cache(id, type, generate_image)
        except ImageGenerationError, e:
            return system_message(str(e))

        return output

    def match_request(self, req):
        return re.match(r'/([a-z]+diag)/.+$', req.path_info)

    def process_request(self, req):
        m = re.match(r'/([a-z]+diag)/(.+)\.(png|svg)$', req.path_info)
        if not m:
            return ""
        id, type = m.group(2), m.group(3)
        data = self.cache.get(id, type)
        if data:
            req.send(data, self.content_types[type], status=200)
        else:
Beispiel #31
0
     elif page_name.startswith(('./', '../')) or \
             page_name in ('.', '..'):
         try:
             # Trac 1.2
             page_name = ws.resolve_relative_name(
                 page_name, referrer)
         except AttributeError:
             page_name = _resolve_relative_name(page_name, referrer)
     else:
         page_name = _resolve_scoped_name(ws, page_name, referrer)
 try:
     page = WikiPage(self.env, page_name, page_version)
 except (TypeError, ValueError):  # Trac < 1.2 (Trac:#11544)
     msg = _('"%(version)s" is not a valid wiki page version.',
             version=page_version)
     return system_message(msg)
 except TracError, e:
     return system_message(e)
 if 'WIKI_VIEW' not in formatter.perm(page.resource):
     return ''
 if not page.exists:
     if page_version:
         return system_message(
             'No version "%s" for wiki page "%s"' %
             (page_version, page_name))
     else:
         return system_message('Wiki page "%s" does not exist' %
                               page_name)
 out = page.text
 if source_section:
     out = self._extract_section(out, source_section)
Beispiel #32
0
         return ''
     try:
         urlf = urllib2.urlopen(source)
         out = urlf.read()  
     except urllib2.URLError, e:
         return system_message('Error while retrieving file', str(e))
     except TracError, e:
         return system_message('Error while previewing', str(e))
     ctxt = Context.from_request(req)
 elif source_format == 'wiki':
     # XXX: Check for recursion in page includes. <NPK>
     if not req.perm.has_permission('WIKI_VIEW'):
         return ''
     page = WikiPage(self.env, source_obj)
     if not page.exists:
         return system_message('Wiki page %s does not exist'%source_obj)
     out = page.text
     ctxt = Context.from_request(req, 'wiki', source_obj)
 elif source_format == 'source':
     if not req.perm.has_permission('FILE_VIEW'):
         return ''
     repo = self.env.get_repository(authname=req.authname)
     node = repo.get_node(source_obj)
     out = node.get_content().read()
     if dest_format is None:
         dest_format = node.content_type or get_mimetype(source_obj, out)
     ctxt = Context.from_request(req, 'source', source_obj)
 # RFE: Add ticket: and comment: sources. <NPK>
 # RFE: Add attachment: source. <NPK>
 else:
     return system_message('Unsupported include source %s'%source)
    def render_macro(self, req, name, content):
        args,kwargs = parse_args(content)
        if len(args) == 0:
            ul = tag.ul(class_="mailinglistlist")

            for mailinglist in Mailinglist.select(self.env):
                if "MAILINGLIST_VIEW" in req.perm(mailinglist.resource):         
                    ul.append(tag.li(tag.a(mailinglist.name,
                                           href=get_resource_url(self.env, mailinglist.resource, req.href))))
                        
            return ul
        if kwargs.has_key('limit'):
            limit = int(kwargs['limit'])
        elif len(args) > 1:
            limit = int(args[1])
        else:
            limit = 10
        resource = Resource("mailinglist",args[0])
        instance = MailinglistSystem(self.env).get_instance_for_resource(resource)

        if isinstance(instance, Mailinglist):
            if not req.perm(instance.resource).has_permission('MAILINGLIST_VIEW'):
                return system_message("Permission denied viewing mailinglist: %s" % instance.name)
            ul = tag.ul()
            for message in instance.messages(limit=limit, insubject=kwargs.get('insubject', None),desc=True):
                ul.append(tag.li(
                    tag.a(tag.span(message.subject, class_="messagesubject"),
                          href=get_resource_url(self.env, message.resource, req.href)),
                    " (",
                    dateinfo(message.date),
                    ")",
                    ))
            ul.append(tag.li(tag.a("(%d messages...)" % instance.count_messages(insubject=kwargs.get('insubject', None)),
                                   href=get_resource_url(self.env, instance.resource, req.href))))
            return tag.div("Mailinglist: ",
                           tag.a(instance.name,
                                 href=get_resource_url(self.env, instance.resource, req.href)),
                           ul,
                           class_="mailinglistfeed")
        elif isinstance(instance, MailinglistMessage):
            if not req.perm(instance.resource).has_permission('MAILINGLIST_VIEW'):
                return system_message("Permission denied viewing mail.")


            else:
                limit = None
            text = wrap_and_quote(instance.body, 78)[0]
            if limit:
                text = "\n".join(text.split("\n")[0:limit])
                textelement = tag.pre(text) + tag.a(tag.pre("(More...)"),
                                                    href=get_resource_url(self.env, instance.resource, req.href))
            else:
                textelement = tag.pre(text)                
            return tag.div(
                tag.div("Mailinglist: ",
                        tag.a(instance.conversation.mailinglist.name,
                              href=get_resource_url(self.env, instance.conversation.mailinglist.resource, req.href))),
                tag.div("Subject: ",
                        tag.a(instance.subject, href=get_resource_url(self.env, instance.resource, req.href))),
                tag.div("From: ",
                        tag.a(instance.from_name, href="mailto:%s" % instance.from_email)),
                tag.div("To: ", instance.to_header),
                tag.div("Date: ", dateinfo(instance.date)),
                tag.div(textelement),
                class_="mailinglistmessage")            
            
        return system_message("Unknown Mailinglist: %s" % content)
Beispiel #34
0
    def expand_macro(self, formatter, name, content, args=None):
        if args is None:
            args = {}

        if name[-4:] in ('_svg', '_png'):
            name, type = name.split('_')
        else:
            type = (args.get('type') or self.env.config.get(
                'blockdiag', 'default_type', 'png')).lower()
            if type not in ('svg', 'png'):
                return system_message(
                    "Invalid type(%s). Type must be 'svg' or 'png'" % type)

        font = self.env.config.get('blockdiag', 'font', '')

        # nonascii unicode can't be passed to hashlib.
        id = make_hash('%s,%s,%s,%r' % (name, type, font, content)).hexdigest()

        ## Create img tag.
        params = {"src": formatter.req.href("%s/%s.%s" % (name, id, type))}
        for key, value in args.iteritems():
            if key != "type":
                params[key] = value
        output = tag.img(**params)

        ## Generate image and cache it.
        def generate_image():
            infile = mktemp(prefix='%s-' % name)
            outfile = mktemp(prefix='%s-' % name)
            try:
                try:
                    f = codecs.open(infile, 'w', 'utf8')
                    try:
                        f.write(content)
                    finally:
                        f.close()
                    cmd = [name, '-a', '-T', type, '-o', outfile, infile]
                    if font:
                        cmd.extend(['-f', font])
                    self.env.log.debug('(%s) command: %r' % (name, cmd))
                    try:
                        proc = Popen(cmd, stderr=PIPE)
                        stderr_value = proc.communicate()[1]
                    except Exception, e:
                        self.env.log.error('(%s) %r' % (name, e))
                        raise ImageGenerationError(
                            "Failed to generate diagram. (%s is not found.)" %
                            name)

                    if proc.returncode != 0 or not os.path.isfile(outfile):
                        self.env.log.error('(%s) %s' % (name, stderr_value))
                        raise ImageGenerationError(
                            "Failed to generate diagram. (rc=%d)" %
                            proc.returncode)
                    f = open(outfile, 'rb')
                    try:
                        data = f.read()
                    finally:
                        f.close()
                    return data
                except ImageGenerationError:
                    raise
                except Exception, e:
                    self.env.log.error('(%s) %r' % (name, e))
                    raise ImageGenerationError("Failed to generate diagram.")
Beispiel #35
0
    def expand_macro(self, formatter, name, content, realms=[]):
        """Evaluate macro call and render results.

        Calls from web-UI come with pre-processed realm selection.
        """
        env = self.env
        req = formatter.req
        tag_system = TagSystem(env)

        all_realms = tag_system.get_taggable_realms()
        if not all_realms:
            # Tag providers are required, no result without at least one.
            return ''
        args, kw = parse_args(content)

        query = args and args[0].strip() or None
        if not realms:
            # Check macro arguments for realms (typical wiki macro call).
            realms = 'realm' in kw and kw['realm'].split('|') or []
        if query:
            # Add realms from query expression.
            realms.extend(query_realms(query, all_realms))
            # Remove redundant realm selection for performance.
            if set(realms) == all_realms:
                query = re.sub('(^|\W)realm:\S+(\W|$)', ' ', query).strip()
        if name == 'TagCloud':
            # Set implicit 'all tagged realms' as default.
            if not realms:
                realms = all_realms
            if query:
                all_tags = collections.Counter()
                # Require per resource query including view permission checks.
                for resource, tags in tag_system.query(req, query):
                    all_tags.update(tags)
            else:
                # Allow faster per tag query, side steps permission checks.
                all_tags = tag_system.get_all_tags(req, realms=realms)
            mincount = 'mincount' in kw and kw['mincount'] or None
            return self.render_cloud(req,
                                     all_tags,
                                     caseless_sort=self.caseless_sort,
                                     mincount=mincount,
                                     realms=realms)
        elif name == 'ListTagged':
            if content and _OBSOLETE_ARGS_RE.search(content):
                data = {'warning': 'obsolete_args'}
            else:
                data = {'warning': None}
            context = formatter.context
            # Use TagsQuery arguments (most likely wiki macro calls).
            cols = 'cols' in kw and kw['cols'] or self.default_cols
            format = 'format' in kw and kw['format'] or self.default_format
            if not realms:
                # Apply ListTagged defaults to macro call w/o realm.
                realms = list(set(all_realms) - set(self.exclude_realms))
                if not realms:
                    return ''
            query = '(%s) (%s)' % (query or '', ' or '.join(
                ['realm:%s' % (r) for r in realms]))
            query_result = tag_system.query(req, query)
            excludes = [
                exc.strip() for exc in kw.get('exclude', '').split(':')
                if exc.strip()
            ]
            if excludes and query_result:
                filtered_result = [(resource, tags)
                                   for resource, tags in query_result
                                   if not any(
                                       fnmatchcase(resource.id, exc)
                                       for exc in excludes)]
                query_result = filtered_result
            if not query_result:
                return ''

            def _link(resource):
                if resource.realm == 'tag':
                    # Keep realm selection in tag links.
                    return builder.a(resource.id,
                                     href=self.get_href(req,
                                                        realms,
                                                        tag=resource))
                elif resource.realm == 'ticket':
                    # Return resource link including ticket status dependend
                    #   class to allow for common Trac ticket link style.
                    ticket = Ticket(env, resource.id)
                    return builder.a('#%s' % ticket.id,
                                     class_=ticket['status'],
                                     href=formatter.href.ticket(ticket.id),
                                     title=shorten_line(ticket['summary']))
                return render_resource_link(env, context, resource, 'compact')

            if format == 'table':
                cols = [
                    col for col in cols.split('|')
                    if col in self.supported_cols
                ]
                # Use available translations from Trac core.
                try:
                    labels = TicketSystem(env).get_ticket_field_labels()
                    labels['id'] = _('Id')
                except AttributeError:
                    # Trac 0.11 neither has the attribute nor uses i18n.
                    labels = {'id': 'Id', 'description': 'Description'}
                labels['realm'] = _('Realm')
                labels['tags'] = _('Tags')
                headers = [{'label': labels.get(col)} for col in cols]
                data.update({'cols': cols, 'headers': headers})

            try:
                results = sorted(
                    query_result,
                    key=lambda r: embedded_numbers(to_unicode(r[0].id)))
            except (InvalidQuery, InvalidTagRealm) as e:
                return system_message(_("ListTagged macro error"), e)
            results = self._paginate(req, results, realms)
            rows = []
            for resource, tags in results:
                desc = tag_system.describe_tagged_resource(req, resource)
                tags = sorted(tags)
                wiki_desc = format_to_oneliner(env, context, desc)
                if tags:
                    rendered_tags = [
                        _link(Resource('tag', tag)) for tag in tags
                    ]
                    if 'oldlist' == format:
                        resource_link = _link(resource)
                    else:
                        resource_link = builder.a(wiki_desc,
                                                  href=get_resource_url(
                                                      env, resource,
                                                      context.href))
                        if 'table' == format:
                            cells = []
                            for col in cols:
                                if col == 'id':
                                    cells.append(_link(resource))
                                # Don't duplicate links to resource in both.
                                elif col == 'description' and 'id' in cols:
                                    cells.append(wiki_desc)
                                elif col == 'description':
                                    cells.append(resource_link)
                                elif col == 'realm':
                                    cells.append(resource.realm)
                                elif col == 'tags':
                                    cells.append(
                                        builder([(tag, ' ')
                                                 for tag in rendered_tags]))
                            rows.append({'cells': cells})
                            continue
                rows.append({
                    'desc': wiki_desc,
                    'rendered_tags': None,
                    'resource_link': _link(resource)
                })
            data.update({
                'format': format,
                'paginator': results,
                'results': rows,
                'tags_url': req.href('tags')
            })

            # Work around a bug in trac/templates/layout.html, that causes a
            # TypeError for the wiki macro call, if we use add_link() alone.
            add_stylesheet(req, 'common/css/search.css')

            return Chrome(env).render_template(req, 'listtagged_results.html',
                                               data, {
                                                   'fragment': True,
                                                   'domain': 'tractags'
                                               })
Beispiel #36
0
    def expand_macro(self, formatter, name, content):
        args = None
        if content:
            content = stripws(content)
            # parse arguments
            # we expect the 1st argument to be a filename (filespec)
            args = [stripws(arg) for arg
                                 in self._split_args_re.split(content)[1::2]]
        if not args:
            return ''
        # strip unicode white-spaces and ZWSPs are copied from attachments
        # section (#10668)
        filespec = args.pop(0)

        # style information
        attr = {}
        style = {}
        link = ''
        # helper for the special case `source:`
        #
        from trac.versioncontrol.web_ui import BrowserModule
        # FIXME: somehow use ResourceSystem.get_known_realms()
        #        ... or directly trac.wiki.extract_link
        try:
            browser_links = [res[0] for res in
                             BrowserModule(self.env).get_link_resolvers()]
        except Exception:
            browser_links = []
        while args:
            arg = args.pop(0)
            if self._size_re.match(arg):
                # 'width' keyword
                attr['width'] = arg
            elif arg == 'nolink':
                link = None
            elif arg.startswith('link='):
                val = arg.split('=', 1)[1]
                elt = extract_link(self.env, formatter.context, val.strip())
                elt = find_element(elt, 'href')
                link = None
                if elt is not None:
                    link = elt.attrib.get('href')
            elif arg in ('left', 'right'):
                style['float'] = arg
            elif arg == 'center':
                style['margin-left'] = style['margin-right'] = 'auto'
                style['display'] = 'block'
                style.pop('margin', '')
            elif arg in ('top', 'bottom', 'middle'):
                style['vertical-align'] = arg
            else:
                match = self._attr_re.match(arg)
                if match:
                    key, val = match.groups()
                    if (key == 'align' and
                            val in ('left', 'right', 'center')) or \
                        (key == 'valign' and
                            val in ('top', 'middle', 'bottom')):
                        args.append(val)
                    elif key in ('margin-top', 'margin-bottom'):
                        style[key] = ' %dpx' % _arg_as_int(val, key, min=1)
                    elif key in ('margin', 'margin-left', 'margin-right') \
                             and 'display' not in style:
                        style[key] = ' %dpx' % _arg_as_int(val, key, min=1)
                    elif key == 'border':
                        style['border'] = ' %dpx solid' % _arg_as_int(val, key)
                    else:
                        m = self._quoted_re.search(val)  # unquote "..." and '...'
                        if m:
                            val = m.group(1)
                        attr[str(key)] = val  # will be used as a __call__ kwd

        if self._quoted_re.match(filespec):
            filespec = filespec.strip('\'"')
        # parse filespec argument to get realm and id if contained.
        parts = [i.strip('\'"')
                 for i in self._split_filespec_re.split(filespec)[1::2]]
        realm = parts[0] if parts else None
        url = raw_url = desc = None
        attachment = None
        interwikimap = InterWikiMap(self.env)
        if realm in ('http', 'https', 'ftp', 'data'):  # absolute
            raw_url = url = filespec
            desc = url.rsplit('?')[0]
        elif realm in interwikimap:
            url, desc = interwikimap.url(realm, ':'.join(parts[1:]))
            raw_url = url
        elif filespec.startswith('//'):       # server-relative
            raw_url = url = filespec[1:]
            desc = url.rsplit('?')[0]
        elif filespec.startswith('/'):        # project-relative
            params = ''
            if '?' in filespec:
                filespec, params = filespec.rsplit('?', 1)
            url = formatter.href(filespec)
            if params:
                url += '?' + params
            raw_url, desc = url, filespec
        elif len(parts) == 3:                 # realm:id:attachment-filename
            #                                 # or intertrac:realm:id
            realm, id, filename = parts
            intertrac_target = "%s:%s" % (id, filename)
            it = formatter.get_intertrac_url(realm, intertrac_target)
            if it:
                url, desc = it
                raw_url = url + unicode_quote('?format=raw')
            else:
                attachment = Resource(realm, id).child('attachment', filename)
        elif len(parts) == 2:
            realm, filename = parts
            if realm in browser_links:  # source:path
                # TODO: use context here as well
                rev = None
                if '@' in filename:
                    filename, rev = filename.rsplit('@', 1)
                url = formatter.href.browser(filename, rev=rev)
                raw_url = formatter.href.browser(filename, rev=rev,
                                                 format='raw')
                desc = filespec
            else:  # #ticket:attachment or WikiPage:attachment
                # FIXME: do something generic about shorthand forms...
                realm = None
                id, filename = parts
                if id and id[0] == '#':
                    realm = 'ticket'
                    id = id[1:]
                elif id == 'htdocs':
                    raw_url = url = formatter.href.chrome('site', filename)
                    desc = os.path.basename(filename)
                elif id == 'shared':
                    raw_url = url = formatter.href.chrome('shared', filename)
                    desc = os.path.basename(filename)
                else:
                    realm = 'wiki'
                if realm:
                    attachment = Resource(realm, id).child('attachment',
                                                           filename)
        elif len(parts) == 1:  # it's an attachment of the current resource
            attachment = formatter.resource.child('attachment', filespec)
        else:
            return system_message(_("No filespec given"))
        if attachment:
            try:
                desc = get_resource_summary(self.env, attachment)
            except ResourceNotFound:
                link = None
                raw_url = chrome_resource_path(formatter.context.req,
                                               'common/attachment.png')
                desc = _('No image "%(id)s" attached to %(parent)s',
                         id=attachment.id,
                         parent=get_resource_name(self.env, attachment.parent))
            else:
                if 'ATTACHMENT_VIEW' in formatter.perm(attachment):
                    url = get_resource_url(self.env, attachment,
                                           formatter.href)
                    raw_url = get_resource_url(self.env, attachment,
                                               formatter.href, format='raw')

        for key in ('title', 'alt'):
            if desc and key not in attr:
                attr[key] = desc
        if style:
            attr['style'] = '; '.join('%s:%s' % (k, escape(v))
                                      for k, v in style.iteritems())
        if not WikiSystem(self.env).is_safe_origin(raw_url,
                                                   formatter.context.req):
            attr['crossorigin'] = 'anonymous'  # avoid password prompt
        result = tag.img(src=raw_url, **attr)
        if link is not None:
            result = tag.a(result, href=link or url,
                           style='padding:0; border:none')
        return result
Beispiel #37
0
class PlantUmlMacro(WikiMacroBase):
    """
    A wiki processor that renders PlantUML diagrams in wiki text.
    
    Example:
    {{{
    {{{
    #!PlantUML
    @startuml
    Alice -> Bob: Authentication Reque
    st
    Bob --> Alice: Authentication Response
    Alice -> Bob: Another authentication Request
    Alice <-- Bob: another authentication Response
    @enduml
    }}}
    }}}
    
    Results in:
    {{{
    #!PlantUML
    @startuml
    Alice -> Bob: Authentication Request
    Bob --> Alice: Authentication Response
    Alice -> Bob: Another authentication Request
    Alice <-- Bob: another authentication Response
    @enduml
    }}}
    """

    implements(IRequestHandler)

    plantuml_jar = Option(
        'plantuml', 'plantuml_jar', '',
        """Path to PlantUML jar file. The jar file can be downloaded from the
          [http://plantuml.sourceforge.net/download.html PlantUML] site.""")
    java_bin = Option(
        'plantuml', 'java_bin', 'java',
        """Path to the Java binary file. The default is `java`, which and
           assumes that the Java binary is on the search path.""")

    def __init__(self):
        self.abs_img_dir = os.path.join(os.path.abspath(self.env.path),
                                        img_dir)
        if not os.path.isdir(self.abs_img_dir):
            os.makedirs(self.abs_img_dir)

    def expand_macro(self, formatter, name, content):
        if not self.plantuml_jar:
            return system_message(
                _("Installation error: plantuml_jar option not defined in trac.ini"
                  ))
        if not os.path.exists(self.plantuml_jar):
            return system_message(
                _("Installation error: plantuml.jar not found at '%s'") %
                self.plantuml_jar)

        # Trac 0.12 supports expand_macro(self, formatter, name, content, args)
        # which allows us to readily differentiate between a WikiProcess and WikiMacro
        # call. To support Trac 0.11, some additional work is required.
        try:
            args = formatter.code_processor.args
        except AttributeError:
            args = None

        path = None
        if not 'path' in args:  #Could be WikiProcessor or WikiMacro call
            if content.strip().startswith("@startuml"):
                markup = content
                path = None
            else:
                path = content
                if not path:
                    return system_message(_("Path not specified"))
        elif args:  #WikiProcessor with args
            path = args.get('path')
            if not path:
                return system_message(_("Path not specified"))

        if path:
            markup, exists = self._read_source_from_repos(formatter, path)
            if not exists:
                return system_message(
                    _("File not found in repository: " + path))
        else:
            if not content:
                return system_message(_("No UML text defined"))
            markup = content.encode('utf-8').strip()

        img_id = hashlib.sha1(markup).hexdigest()
        if not self._is_img_existing(img_id):
            cmd = '%s -jar -Djava.awt.headless=true "%s" -charset UTF-8 -pipe' % (
                self.java_bin, self.plantuml_jar)
            p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
            (img_data, stderr) = p.communicate(input=markup)
            if p.returncode != 0:
                return system_message(
                    _("Error running plantuml: '%s'") % stderr)
            self._write_img_to_file(img_id, img_data)

        link = formatter.href('plantuml', id=img_id)
        return tag.img(src=link)

    def get_macros(self):
        yield 'plantuml'  #WikiProcessor syntax
        yield 'PlantUml'  #WikiMacros syntax
        yield 'PlantUML'  #deprecated, retained for backward compatibility

    # IRequestHandler
    def match_request(self, req):
        return re.match(r'/plantuml?$', req.path_info)

    def process_request(self, req):
        img_id = req.args.get('id')
        img_data = self._read_img_from_file(img_id)
        req.send(img_data, 'image/png', status=200)
        return ""

    # Internal
    def _get_img_path(self, img_id):
        img_path = os.path.join(self.abs_img_dir, img_id)
        img_path += '.png'
        return img_path

    def _is_img_existing(self, img_id):
        img_path = self._get_img_path(img_id)
        return os.path.isfile(img_path)

    def _write_img_to_file(self, img_id, data):
        img_path = self._get_img_path(img_id)
        open(img_path, 'wb').write(data)

    def _read_img_from_file(self, img_id):
        img_path = self._get_img_path(img_id)
        img_data = open(img_path, 'rb').read()
        return img_data

    def _read_source_from_repos(self, formatter, src_path):
        repos_mgr = RepositoryManager(self.env)
        try:  #0.12+
            repos_name, repos, source_obj = repos_mgr.get_repository_by_path(
                src_path)
        except AttributeError, e:  #0.11
            repos = repos_mgr.get_repository(formatter.req.authname)
        path, rev = _split_path(src_path)
        if repos.has_node(path, rev):
            node = repos.get_node(path, rev)
            content = node.get_content().read()
            exists = True
        else:
            rev = rev or repos.get_youngest_rev()
            # TODO: use `raise NoSuchNode(path, rev)`
            content = system_message(
                _("No such node '%s' at revision '%s'") % (path, rev))
            exists = False

        return (content, exists)