Beispiel #1
0
 def _check_quickjump(self, req, noquickjump, kwd):
     """Look for search shortcuts"""
     # Source quickjump  FIXME: delegate to ISearchSource.search_quickjump
     quickjump_href = None
     if kwd[0] == '/':
         quickjump_href = req.href.browser(kwd)
         name = kwd
         description = _('Browse repository path %(path)s', path=kwd)
     else:
         context = web_context(req, 'search')
         link = find_element(extract_link(self.env, context, kwd), 'href')
         if link is not None:
             quickjump_href = link.attrib.get('href')
             name = link.children
             description = link.attrib.get('title', '')
     if quickjump_href:
         # Only automatically redirect to local quickjump links
         if not quickjump_href.startswith(req.base_path or '/'):
             noquickjump = True
         if noquickjump:
             return {'href': quickjump_href, 'name': tag.em(name),
                     'description': description}
         else:
             help_url = req.href.wiki('TracSearch') + '#Quicksearches'
             search_url = req.href.search(q=kwd, noquickjump=1)
             # FIXME: use tag_
             add_notice(req, Markup(_(
                 'You arrived here through the <a href="%(help_url)s">'
                 'quick-jump</a> search feature. To instead search for the '
                 'term <strong>%(term)s</strong>, click <a '
                 'href="%(search_url)s">here</a>.',
                 help_url=escape(help_url), term=escape(kwd),
                 search_url=escape(search_url))))
             req.redirect(quickjump_href)
Beispiel #2
0
 def _render_line(self, line):
     hidden = line["type"] in self.show_msg_types and " " or "hidden"
     line.update(
         {
             "time": line.get("timestamp") and line["timestamp"].time() or "",
             "message": escape(line["message"]),
             "comment": escape(line.get("comment")),
             "action": escape(line.get("action")),
             "hidden": hidden,
         }
     )
     if line["type"] == "comment":
         return (
             '<tr class="%(type)s %(hidden)s"><td class="time">'
             + '[<a name="%(time)s" href="#%(time)s">%(time)s</a>]'
             + '</td><td class="left %(nickcls)s">&lt;%(nick)s&gt;'
             + '</td><td class="right">%(comment)s</td></tr>'
         ) % line
     if line["type"] == "action":
         return (
             '<tr class="%(type)s %(hidden)s"><td class="time">'
             + '[<a name="%(time)s" href="#%(time)s">%(time)s</a>]'
             + '</td><td class="left">*</td><td class="right">'
             + "%(action)s</td></tr>"
         ) % line
     else:
         return (
             '<tr class="%(type)s %(hidden)s"><td class="time">'
             + '[<a name="%(time)s" href="#%(time)s">%(time)s</a>]'
             + '</td><td class="left"></td><td class='
             + '"right">%(message)s</td></tr>'
         ) % line
Beispiel #3
0
 def _render_line(self, line):
     hidden = line['type'] in self.show_msg_types and ' ' or 'hidden'
     line.update({
         'time':
         line.get('timestamp') and line['timestamp'].time() or '',
         'message':
         escape(line['message']),
         'comment':
         escape(line.get('comment')),
         'action':
         escape(line.get('action')),
         'hidden':
         hidden,
     })
     if line['type'] == 'comment':
         return ('<tr class="%(type)s %(hidden)s"><td class="time">' + \
                '[<a name="%(time)s" href="#%(time)s">%(time)s</a>]' + \
                '</td><td class="left %(nickcls)s">&lt;%(nick)s&gt;' + \
                '</td><td class="right">%(comment)s</td></tr>')%line
     if line['type'] == 'action':
         return ('<tr class="%(type)s %(hidden)s"><td class="time">' + \
                '[<a name="%(time)s" href="#%(time)s">%(time)s</a>]' + \
                '</td><td class="left">*</td><td class="right">' + \
                '%(action)s</td></tr>')%line
     else:
         return ('<tr class="%(type)s %(hidden)s"><td class="time">' + \
                '[<a name="%(time)s" href="#%(time)s">%(time)s</a>]' + \
                '</td><td class="left"></td><td class=' + \
                '"right">%(message)s</td></tr>')%line
Beispiel #4
0
 def add_value(prefix, value):
     if value is None:
         return
     if value in (True, False):
         set_str(prefix, int(value))
     elif isinstance(value, (Markup, Fragment)):
         set_unicode(prefix, unicode(value))
     elif isinstance(value, str):
         if do_escape:
             # Assume UTF-8 here, for backward compatibility reasons
             set_unicode(prefix, escape(to_unicode(value)))
         else:
             set_str(prefix, value)
     elif isinstance(value, unicode):
         if do_escape:
             set_unicode(prefix, escape(value))
         else:
             set_unicode(prefix, value)
     elif isinstance(value, dict):
         for k in value.keys():
             add_value('%s.%s' % (prefix, to_unicode(k)), value[k])
     else:
         if hasattr(value, '__iter__') or \
                 isinstance(value, (list, tuple)):
             for idx, item in enumerate(value):
                 add_value('%s.%d' % (prefix, idx), item)
         else:
             set_str(prefix, value)
Beispiel #5
0
 def add_value(prefix, value):
     if value is None:
         return
     if value in (True, False):
         set_str(prefix, int(value))
     elif isinstance(value, (Markup, Fragment)):
         set_unicode(prefix, unicode(value))
     elif isinstance(value, str):
         if do_escape:
             # Assume UTF-8 here, for backward compatibility reasons
             set_unicode(prefix, escape(to_unicode(value)))
         else:
             set_str(prefix, value)
     elif isinstance(value, unicode):
         if do_escape:
             set_unicode(prefix, escape(value))
         else:
             set_unicode(prefix, value)
     elif isinstance(value, dict):
         for k in value.keys():
             add_value('%s.%s' % (prefix, to_unicode(k)), value[k])
     else:
         if hasattr(value, '__iter__') or \
                 isinstance(value, (list, tuple)):
             for idx, item in enumerate(value):
                 add_value('%s.%d' % (prefix, idx), item)
         else:
             set_str(prefix, value)
Beispiel #6
0
    def expand_macro(self, formatter, name, args):
        env = formatter.env
        req = formatter.req

        arg = args.split('||')
        duedate = arg[0].strip()
        summary = arg[1].strip()
        effort = arg[2].strip()
        user = req.args['user']
        project = req.args['project']

        db = env.get_db_cnx()
        cursor = db.cursor()

        sql = "SELECT id, owner, summary, description, status FROM ticket WHERE summary=%s"
        cursor.execute(sql, [summary])

        row = cursor.fetchone()
        if row == None:
            link = html.A(
                escape(summary),
                href=
                "%s?field_summary=%s&field_type=task&field_duedate=%s&field_effort=%s&field_owner=%s&field_project=%s"
                % (env.href.newticket(), escape(summary), escape(duedate),
                   escape(effort), escape(user), escape(project)))
        else:
            user = row[1]
            link = html.A("#%s %s" % (row[0], escape(row[2])),
                          href=env.href.ticket(row[0]),
                          class_=row[4])

        req.args['effort'] = req.args['effort'] + float(effort)

        return html.TR(html.TD(escape(user)), html.TD(duedate), html.TD(link),
                       html.TD(effort))
Beispiel #7
0
 def _make_link(self, ns, target, match, label):
     # first check for an alias defined in trac.ini
     ns = self.env.config.get('intertrac', ns) or ns
     if ns in self.wiki.link_resolvers:
         if ns == 'wiki':
             return '[[%s|%s]]'%(escape(label, False), target)
         else:
             return self.wiki.link_resolvers[ns](self, ns, target,
                                                 escape(label, False))
     elif target.startswith('//') or ns == "mailto":
         return self._make_ext_link(ns+':'+target, label)
     else:
         return self._make_intertrac_link(ns, target, label) or \
                self._make_interwiki_link(ns, target, label) or \
                match
Beispiel #8
0
 def _make_link(self, ns, target, match, label):
     # first check for an alias defined in trac.ini
     ns = self.env.config.get('intertrac', ns) or ns
     if ns in self.wiki.link_resolvers:
         if ns == 'wiki':
             return '[[%s|%s]]' % (escape(label, False), target)
         else:
             return self.wiki.link_resolvers[ns](self, ns, target,
                                                 escape(label, False))
     elif target.startswith('//') or ns == "mailto":
         return self._make_ext_link(ns + ':' + target, label)
     else:
         return self._make_intertrac_link(ns, target, label) or \
                self._make_interwiki_link(ns, target, label) or \
                match
Beispiel #9
0
 def render_property(self, name, mode, context, props):
     # No special treatment besides respecting newlines in values.
     value = props[name]
     if value and '\n' in value:
         value = Markup(''.join(['<br />%s' % escape(v)
                                 for v in value.split('\n')]))
     return value
Beispiel #10
0
    def render_macro(self, req, name, content):
        from trac.wiki.formatter import wiki_to_outline
        min_depth, max_depth = 1, 6
        title = None
        inline = 0
        if content:
            argv = [arg.strip() for arg in content.split(',')]
            if len(argv) > 0:
                depth = argv[0]
                if depth.find('-') >= 0:
                    min_depth, max_depth = [int(d) for d in depth.split('-', 1)]
                else:
                    min_depth, max_depth = int(depth), int(depth)
                if len(argv) > 1:
                    title = argv[1].strip()
                    if len(argv) > 2:
                        inline = argv[2].strip().lower() == 'inline'

        db = self.env.get_db_cnx()
        cursor = db.cursor()
        pagename = req.args.get('page') or 'WikiStart'
        page = WikiPage(self.env, pagename)

        buf = StringIO()
        if not inline:
            buf.write('<div class="wiki-toc">')
        if title:
            buf.write('<h4>%s</h4>' % escape(title))
        buf.write(wiki_to_outline(page.text, self.env, db=db,
                                  max_depth=max_depth, min_depth=min_depth))
        if not inline:
            buf.write('</div>')
        return buf.getvalue()
Beispiel #11
0
    def render_macro(self, req, name, content):
        from trac.wiki.formatter import wiki_to_outline
        min_depth, max_depth = 1, 6
        title = None
        inline = 0
        if content:
            argv = [arg.strip() for arg in content.split(',')]
            if len(argv) > 0:
                depth = argv[0]
                if depth.find('-') >= 0:
                    min_depth, max_depth = [int(d) for d in depth.split('-', 1)]
                else:
                    min_depth, max_depth = int(depth), int(depth)
                if len(argv) > 1:
                    title = argv[1].strip()
                    if len(argv) > 2:
                        inline = argv[2].strip().lower() == 'inline'

        db = self.env.get_db_cnx()
        cursor = db.cursor()
        pagename = req.args.get('page') or 'WikiStart'
        page = WikiPage(self.env, pagename)

        buf = StringIO()
        if not inline:
            buf.write('<div class="wiki-toc">')
        if title:
            buf.write('<h4>%s</h4>' % escape(title))
        buf.write(wiki_to_outline(page.text, self.env, db=db,
                                  max_depth=max_depth, min_depth=min_depth))
        if not inline:
            buf.write('</div>')
        return buf.getvalue()
Beispiel #12
0
def render_node_property(env, name, value):
    """Renders a node property value to HTML.

    Currently only handle multi-line properties. See also #1601.
    """
    if value and '\n' in value:
        value = Markup(''.join(['<br />%s' % escape(v)
                                for v in value.split('\n')]))
    return value
Beispiel #13
0
def render_node_property(env, name, value):
    """Renders a node property value to HTML.

    Currently only handle multi-line properties. See also #1601.
    """
    if value and '\n' in value:
        value = Markup(''.join(
            ['<br />%s' % escape(v) for v in value.split('\n')]))
    return value
Beispiel #14
0
    def render(self, req, mimetype, content, filename=None, url=None):
        if is_binary(content):
            self.env.log.debug("Binary data; no preview available")
            return

        self.env.log.debug("Using default plain text mimeviewer")
        content = content_to_unicode(self.env, content, mimetype)
        for line in content.splitlines():
            yield escape(line)
Beispiel #15
0
    def render(self, req, mimetype, content, filename=None, url=None):
        if is_binary(content):
            self.env.log.debug("Binary data; no preview available")
            return

        self.env.log.debug("Using default plain text mimeviewer")
        content = content_to_unicode(self.env, content, mimetype)
        for line in content.splitlines():
            yield escape(line)
Beispiel #16
0
 def _error_div(self, msg):
     """Display msg in an error box, using Trac style."""
     self.log.error(msg)
     if isinstance(msg, str):
         msg = tag.pre(escape(msg))
     return tag.div(tag.strong(
         _("Graphviz macro processor has detected an error. "
           "Please fix the problem before continuing.")),
                    msg,
                    class_="system-message")
Beispiel #17
0
 def render(self, context, mimetype, content, filename=None, rev=None):
     output = render_textile(content)
     if WikiSystem(self.env).render_unsafe_content:
         return Markup(output)
     try:
         return self._sanitizer.sanitize(output)
     except HTMLParseError as e:
         self.log.warning(e)
         line = content.splitlines()[e.lineno - 1].strip()
         return system_message(_("HTML parsing error: %(message)s",
                                 message=escape(e.msg)), line)
Beispiel #18
0
 def runTest(self):
     """Admin create duplicate version"""
     name = "DuplicateVersion"
     self._tester.create_version(name)
     version_admin = self._tester.url + "/admin/ticket/versions"
     tc.go(version_admin)
     tc.url(version_admin)
     tc.formvalue('addversion', 'name', name)
     tc.submit()
     tc.notfind(internal_error)
     tc.find(escape('Version "%s" already exists.' % name))
Beispiel #19
0
 def _error_div(self, msg):
     """Display msg in an error box, using Trac style."""
     if isinstance(msg, str):
         msg = to_unicode(msg)
     self.log.error(msg)
     if isinstance(msg, unicode):
         msg = tag.pre(escape(msg))
     return tag.div(
             tag.strong(_("Graphviz macro processor has detected an error. "
                          "Please fix the problem before continuing.")),
             msg, class_="system-message")
Beispiel #20
0
def add_warning(req, msg, *args):
    """Add a non-fatal warning to the request object.

    When rendering pages, all warnings will be rendered to the user. Note that
    the message is escaped (and therefore converted to `Markup`) before it is
    stored in the request object.
    """
    if args:
        msg %= args
    msg = escape(msg, False)
    if msg not in req.chrome['warnings']:
        req.chrome['warnings'].append(msg)
Beispiel #21
0
def add_notice(req, msg, *args):
    """Add an informational notice to the request object.

    When rendering pages, all notices will be rendered to the user. Note that
    the message is escaped (and therefore converted to `Markup`) before it is
    stored in the request object.
    """
    if args:
        msg %= args
    msg = escape(msg, False)
    if msg not in req.chrome['notices']:
        req.chrome['notices'].append(msg)
Beispiel #22
0
 def format(self, transport, style, event):
     if transport != 'email':
         return
     text = event.target.text
     if style == 'text/plain':
         if 'raise-text-plain' in text:
             raise ValueError()
         return unicode(text)
     if style == 'text/html':
         if 'raise-text-html' in text:
             raise ValueError()
         return u'<p>%s</p>' % escape(text)
Beispiel #23
0
 def visit_system_message(self, node):
     paragraph = node.children.pop(0)
     message = escape(paragraph.astext()) if paragraph else ''
     backrefs = node['backrefs']
     if backrefs:
         span = ('<span class="system-message">%s</span>' %
                 (''.join('<a href="#%s" title="%s">?</a>' %
                          (backref, message)
                          for backref in backrefs)))
     else:
         span = ('<span class="system-message" title="%s">?</span>'
                 % message)
     self.body.append(span)
Beispiel #24
0
 def render_macro(self, req, name, content):
     text = ""
     if content:
         lines = content.split("\n")
         if lines[0].startswith("cols:"):
             try:
                 width = int(lines[0][5:].strip())
                 lines.pop(0)
             except ValueError:
                 width = 72
         else:
             width = 72
         text = wrap("\n".join(lines), cols=width)
     return Markup("<pre class='wiki'>%s</pre>" % escape(text))
Beispiel #25
0
    def process_request(self, req):
        from tractags.macros import TagMacros
        from tractags.parseargs import parseargs
        from trac.web.chrome import add_stylesheet

        req.perm.require('TAGS_VIEW')

        add_stylesheet(req, 'tags/css/tractags.css')
        data = {}

        def update_from_req(args):
            for k in req.args.keys():
                args[k] = unicode(req.args.get(k))

        if not req.args.has_key('e') and re.match('^/tags/?$', req.path_info):
            index = self.env.config.get('tags', 'index', 'cloud')
            index_kwargs = {'smallest': 10, 'biggest': 30}
            _, config_kwargs = parseargs(
                self.env.config.get('tags', 'index.args', ''))
            index_kwargs.update(config_kwargs)
            update_from_req(index_kwargs)

            if index == 'cloud':
                data['tag_body'] = Markup(
                    TagMacros(self.env).render_tagcloud(req, **index_kwargs))
            elif index == 'list':
                data['tag_body'] = Markup(
                    TagMacros(self.env).render_listtagged(req, **index_kwargs))
            else:
                raise TracError("Invalid index style '%s'" % index)
        else:
            _, args = parseargs(self.env.config.get('tags', 'listing.args',
                                                    ''))
            if req.args.has_key('e'):
                expr = req.args.get('e')
            else:
                expr = req.path_info[6:]
            data['tag_title'] = Markup(
                'Objects matching the expression <i>%s</i>' % escape(expr))
            data['tag_expression'] = expr
            try:
                Expression(expr)
            except Exception, e:
                data['tag_expression_error'] = unicode(e).replace(
                    ' (line 1)', '')
            args['expression'] = expr
            tags = []
            update_from_req(args)
            data['tag_body'] = Markup(
                TagMacros(self.env).render_listtagged(req, *tags, **args))
Beispiel #26
0
 def render_macro(self, req, name, content):
     text = ''
     if content:
         lines = content.split('\n')
         if lines[0].startswith('cols:'):
             try:
                 width = int(lines[0][5:].strip())
                 lines.pop(0)
             except ValueError:
                 width = 72
         else:
             width = 72
         text = wrap('\n'.join(lines), cols=width)
     return Markup("<pre class='wiki'>%s</pre>" % escape(text))
Beispiel #27
0
 def expand_macro(self, formatter, name, content):
     _, kw = parse_args(content)
     name = kw.get('name', 'Unknown')
     artist = kw.get('artist', 'Unknown')
     song_title = escape("%s - %s"%(name, artist))
     wiki = kw.get('wiki', formatter.resource.id)
     attachment = kw.get('attachment')
     song_url = "/raw-attachment/wiki/%s/%s"%(wiki, attachment)
     data = Chrome(self.env).populate_data(formatter.req, {
         'song_url': song_url,
         'song_title': song_title,
         'name': name,
         'artist': artist
     });
     return Chrome(self.env).load_template('xspf-slim.html').\
             generate(**data)
Beispiel #28
0
    def process_request(self, req):
        from tractags.macros import TagMacros
        from tractags.parseargs import parseargs
        from trac.web.chrome import add_stylesheet

        req.perm.require('TAGS_VIEW')

        add_stylesheet(req, 'tags/css/tractags.css')
        data = {}

        def update_from_req(args):
            for k in req.args.keys():
                args[k] = unicode(req.args.get(k))

        if not req.args.has_key('e') and re.match('^/tags/?$', req.path_info):
            index = self.env.config.get('tags', 'index', 'cloud')
            index_kwargs = {'smallest': 10, 'biggest': 30}
            _, config_kwargs = parseargs(self.env.config.get('tags', 'index.args', ''))
            index_kwargs.update(config_kwargs)
            update_from_req(index_kwargs)

            if index == 'cloud':
                data['tag_body'] = Markup(
                    TagMacros(self.env).render_tagcloud(req, **index_kwargs))
            elif index == 'list':
                data['tag_body'] = Markup(
                    TagMacros(self.env).render_listtagged(req, **index_kwargs))
            else:
                raise TracError("Invalid index style '%s'" % index)
        else:
            _, args = parseargs(self.env.config.get('tags', 'listing.args', ''))
            if req.args.has_key('e'):
                expr = req.args.get('e')
            else:
                expr = req.path_info[6:]
            data['tag_title'] = Markup('Objects matching the expression <i>%s</i>' % escape(expr))
            data['tag_expression'] = expr
            try:
                Expression(expr)
            except Exception, e:
                data['tag_expression_error'] = unicode(e).replace(' (line 1)', '')
            args['expression'] = expr
            tags = []
            update_from_req(args)
            data['tag_body'] = Markup(
                TagMacros(self.env).render_listtagged(req, *tags, **args))
    def expand_macro(self, formatter, name, content):
        
        arg,kwarg = parse_args(content)
        
        includepattern = kwarg.get('include', '')
        #excludepattern = kwarg.get('exclude', '')
        length = int(kwarg.get('max', -1))
        ignorenoduedate = kwarg.get('ignore') == 'noduedate' or None
        
        if length==-1:
            length = None
        
        out = StringIO()
        
        include = re.compile(includepattern)
        #exclude = re.compile(excludepattern)

        milestones = [milestone
                      for milestone in Milestone.select(self.env, include_completed=False)
                      if include.match(milestone.name)]
                
        out.write('<ul>\n')
        for milestone in milestones[0:length]:

            if milestone.due:
                #TODO: add one day to tdelta
                tdelta = (to_timestamp(milestone.due) -
                          to_timestamp(datetime.now(formatter.req.tz)))
                if tdelta > 0:
                    date = format_date(milestone.due, '%Y-%m-%d',
                                       formatter.req.tz)
                else:
                    date = None
            elif not ignorenoduedate:
                date = Markup('<i>(Unspecified)</i>')
            else:
                date = None
                
            if date:        
                out.write('<li>%s - <a href="%s">%s</a></li>\n' % 
                          (date, self.env.href.milestone(milestone.name),
                           escape(milestone.name)))
            
        out.write('</ul>\n')
        return Markup(out.getvalue())
Beispiel #30
0
 def _format_link(formatter, ns, name, label):
     segments = name.split('#')
     name = segments[0]
     step = len(segments) == 2 and segments[1] or ''
     try:
         name = int(name)
     except ValueError:
         return label
     build = Build.fetch(self.env, name)
     if build:
         config = BuildConfig.fetch(self.env, build.config)
         title = 'Build %d ([%s] of %s) by %s' % (build.id, build.rev,
                 config.label, build.slave)
         if step:
             if not step.startswith('step_'):
                 step = 'step_' + step
             step = '#' + escape(step)
         return '<a class="build" href="%s" title="%s">%s</a>' \
                % (formatter.href.build(build.config, build.id) + step,
                     title, label)
     return label
Beispiel #31
0
 def _format_link(formatter, ns, name, label):
     segments = name.split('#')
     name = segments[0]
     step = len(segments) == 2 and segments[1] or ''
     try:
         name = int(name)
     except ValueError:
         return label
     build = Build.fetch(self.env, name)
     if build:
         config = BuildConfig.fetch(self.env, build.config)
         title = 'Build %d ([%s] of %s) by %s' % (
             build.id, build.rev, config.label, build.slave)
         if step:
             if not step.startswith('step_'):
                 step = 'step_' + step
             step = '#' + escape(step)
         return '<a class="build" href="%s" title="%s">%s</a>' \
                % (formatter.href.build(build.config, build.id) + step,
                     title, label)
     return label
Beispiel #32
0
    def expand_macro(self, formatter, name, args):
        env = formatter.env
        req = formatter.req

        arg = args.split('||')
        duedate = arg[0].strip()
        summary = arg[1].strip()
        effort = arg[2].strip()
        user = req.args['user']
        project = req.args['project']

        db = env.get_db_cnx()
        cursor = db.cursor()

        sql = "SELECT id, owner, summary, description, status FROM ticket WHERE summary=%s"
        cursor.execute(sql, [summary]) 

        row = cursor.fetchone()
        if row==None:     
            link = html.A(escape(summary), href="%s?field_summary=%s&field_type=task&field_duedate=%s&field_effort=%s&field_owner=%s&field_project=%s" %   
                                                    (env.href.newticket(),
                                                    escape(summary),
                                                    escape(duedate),
                                                    escape(effort),
                                                    escape(user),
                                                    escape(project)))
        else:
            user = row[1]
            link = html.A("#%s %s" % (row[0],escape(row[2])), href=env.href.ticket(row[0]), class_=row[4])

        req.args['effort'] = req.args['effort'] + float(effort)

        return html.TR(
            html.TD(escape(user)),
            html.TD(duedate),
            html.TD(link),
            html.TD(effort)
        )         
Beispiel #33
0
    def _diff_to_hdf(self, difflines, tabwidth):
        """
        Translate a diff file into something suitable for inclusion in HDF.
        The result is [(filename, revname_old, revname_new, changes)],
        where changes has the same format as the result of
        `trac.versioncontrol.diff.hdf_diff`.

        If the diff cannot be parsed, this method returns None.
        """
        def _markup_intraline_change(fromlines, tolines):
            from trac.versioncontrol.diff import _get_change_extent
            for i in xrange(len(fromlines)):
                fr, to = fromlines[i], tolines[i]
                (start, end) = _get_change_extent(fr, to)
                if start != 0 or end != 0:
                    last = end + len(fr)
                    fromlines[i] = fr[:start] + '\0' + fr[start:last] + \
                                   '\1' + fr[last:]
                    last = end + len(to)
                    tolines[i] = to[:start] + '\0' + to[start:last] + \
                                 '\1' + to[last:]

        import re
        space_re = re.compile(' ( +)|^ ')

        def htmlify(match):
            div, mod = divmod(len(match.group(0)), 2)
            return div * '&nbsp; ' + mod * '&nbsp;'

        output = []
        filename, groups = None, None
        lines = iter(difflines)
        try:
            line = lines.next()
            while True:
                if not line.startswith('--- '):
                    line = lines.next()
                    continue

                # Base filename/version
                words = line.split(None, 2)
                filename, fromrev = words[1], 'old'
                groups, blocks = None, None

                # Changed filename/version
                line = lines.next()
                if not line.startswith('+++ '):
                    return None

                words = line.split(None, 2)
                if len(words[1]) < len(filename):
                    # Always use the shortest filename for display
                    filename = words[1]
                groups = []
                output.append({
                    'filename': filename,
                    'oldrev': fromrev,
                    'newrev': 'new',
                    'diff': groups
                })

                for line in lines:
                    # @@ -333,10 +329,8 @@
                    r = re.match(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@', line)
                    if not r:
                        break
                    blocks = []
                    groups.append(blocks)
                    fromline, fromend, toline, toend = map(int, r.groups())
                    last_type = None

                    fromend += fromline
                    toend += toline

                    while fromline < fromend or toline < toend:
                        line = lines.next()

                        # First character is the command
                        command = ' '
                        if line:
                            command, line = line[0], line[1:]
                        # Make a new block?
                        if (command == ' ') != last_type:
                            last_type = command == ' '
                            blocks.append({
                                'type': last_type and 'unmod' or 'mod',
                                'base.offset': fromline - 1,
                                'base.lines': [],
                                'changed.offset': toline - 1,
                                'changed.lines': []
                            })
                        if command == ' ':
                            blocks[-1]['changed.lines'].append(line)
                            blocks[-1]['base.lines'].append(line)
                            fromline += 1
                            toline += 1
                        elif command == '+':
                            blocks[-1]['changed.lines'].append(line)
                            toline += 1
                        elif command == '-':
                            blocks[-1]['base.lines'].append(line)
                            fromline += 1
                        else:
                            return None
                line = lines.next()
        except StopIteration:
            pass

        # Go through all groups/blocks and mark up intraline changes, and
        # convert to html
        for o in output:
            for group in o['diff']:
                for b in group:
                    f, t = b['base.lines'], b['changed.lines']
                    if b['type'] == 'mod':
                        if len(f) == 0:
                            b['type'] = 'add'
                        elif len(t) == 0:
                            b['type'] = 'rem'
                        elif len(f) == len(t):
                            _markup_intraline_change(f, t)
                    for i in xrange(len(f)):
                        line = f[i].expandtabs(tabwidth)
                        line = escape(line)
                        line = '<del>'.join([
                            space_re.sub(htmlify, seg)
                            for seg in line.split('\0')
                        ])
                        line = line.replace('\1', '</del>')
                        f[i] = Markup(line)
                    for i in xrange(len(t)):
                        line = t[i].expandtabs(tabwidth)
                        line = escape(line)
                        line = '<ins>'.join([
                            space_re.sub(htmlify, seg)
                            for seg in line.split('\0')
                        ])
                        line = line.replace('\1', '</ins>')
                        t[i] = Markup(line)
        return output
Beispiel #34
0
    def render_macro(self, req, name, content):
        # args will be null if the macro is called without parenthesis.
        if not content:
            return ''
        # parse arguments
        # we expect the 1st argument to be a filename (filespec)
        args = content.split(',')
        if len(args) == 0:
            raise Exception("No argument.")
        filespec = args[0]
        size_re = re.compile('[0-9]+%?$')
        attr_re = re.compile('(align|border|width|height|alt'
                             '|title|longdesc|class|id|usemap)=(.+)')
        quoted_re = re.compile("(?:[\"'])(.*)(?:[\"'])$")
        attr = {}
        style = {}
        nolink = False
        for arg in args[1:]:
            arg = arg.strip()
            if size_re.match(arg):
                # 'width' keyword
                attr['width'] = arg
                continue
            if arg == 'nolink':
                nolink = True
                continue
            if arg in ('left', 'right', 'top', 'bottom'):
                style['float'] = arg
                continue
            match = attr_re.match(arg)
            if match:
                key, val = match.groups()
                m = quoted_re.search(val)  # unquote "..." and '...'
                if m:
                    val = m.group(1)
                if key == 'align':
                    style['float'] = val
                elif key == 'border':
                    style['border'] = ' %dpx solid' % int(val)
                else:
                    attr[str(key)] = val  # will be used as a __call__ keyword

        # parse filespec argument to get module and id if contained.
        parts = filespec.split(':')
        url = None
        if len(parts) == 3:  # module:id:attachment
            if parts[0] in ['wiki', 'ticket']:
                module, id, file = parts
            else:
                raise Exception("%s module can't have attachments" % parts[0])
        elif len(parts) == 2:
            from trac.versioncontrol.web_ui import BrowserModule
            try:
                browser_links = [
                    link for link, _ in BrowserModule(
                        self.env).get_link_resolvers()
                ]
            except Exception:
                browser_links = []
            if parts[0] in browser_links:  # source:path
                module, file = parts
                rev = None
                if '@' in file:
                    file, rev = file.split('@')
                url = req.href.browser(file, rev=rev)
                raw_url = req.href.browser(file, rev=rev, format='raw')
                desc = filespec
            else:  # #ticket:attachment or WikiPage:attachment
                # FIXME: do something generic about shorthand forms...
                id, file = parts
                if id and id[0] == '#':
                    module = 'ticket'
                    id = id[1:]
                elif id == 'htdocs':
                    raw_url = url = req.href.chrome('site', file)
                    desc = os.path.basename(file)
                elif id in ('http', 'https', 'ftp'):  # external URLs
                    raw_url = url = desc = id + ':' + file
                else:
                    module = 'wiki'
        elif len(parts) == 1:  # attachment
            # determine current object
            # FIXME: should be retrieved from the formatter...
            # ...and the formatter should be provided to the macro
            file = filespec
            module, id = 'wiki', 'WikiStart'
            path_info = req.path_info.split('/', 2)
            if len(path_info) > 1:
                module = path_info[1]
            if len(path_info) > 2:
                id = path_info[2]
            if module not in ['wiki', 'ticket']:
                raise Exception('Cannot reference local attachment from here')
        else:
            raise Exception('No filespec given')
        if not url:  # this is an attachment
            from trac.attachment import Attachment
            attachment = Attachment(self.env, module, id, file)
            url = attachment.href(req)
            raw_url = attachment.href(req, format='raw')
            desc = attachment.description
        for key in ['title', 'alt']:
            if desc and not attr.has_key(key):
                attr[key] = desc
        if style:
            attr['style'] = '; '.join(
                ['%s:%s' % (k, escape(v)) for k, v in style.iteritems()])
        result = Markup(html.IMG(src=raw_url, **attr)).sanitize()
        if not nolink:
            result = html.A(result, href=url, style='padding:0; border:none')
        return result
Beispiel #35
0
 def runTest(self):
     """Admin create duplicate severity"""
     name = "DuplicateSeverity"
     self._tester.create_severity(name)
     self._tester.create_severity(name)
     tc.find(escape('Severity value "%s" already exists' % name))
Beispiel #36
0
    def render_macro(self, req, name, content):
        # args will be null if the macro is called without parenthesis.
        if not content:
            return ""
        # parse arguments
        # we expect the 1st argument to be a filename (filespec)
        args = content.split(",")
        if len(args) == 0:
            raise Exception(u"Aucun argument")
        filespec = args[0]
        size_re = re.compile("[0-9]+%?$")
        attr_re = re.compile("(align|border|width|height|alt" "|title|longdesc|class|id|usemap)=(.+)")
        quoted_re = re.compile("(?:[\"'])(.*)(?:[\"'])$")
        attr = {}
        style = {}
        nolink = False
        for arg in args[1:]:
            arg = arg.strip()
            if size_re.match(arg):
                # 'width' keyword
                attr["width"] = arg
                continue
            if arg == "nolink":
                nolink = True
                continue
            if arg in ("left", "right", "top", "bottom"):
                style["float"] = arg
                continue
            match = attr_re.match(arg)
            if match:
                key, val = match.groups()
                m = quoted_re.search(val)  # unquote "..." and '...'
                if m:
                    val = m.group(1)
                if key == "align":
                    style["float"] = val
                elif key == "border":
                    style["border"] = " %dpx solid" % int(val)
                else:
                    attr[str(key)] = val  # will be used as a __call__ keyword

        # parse filespec argument to get module and id if contained.
        parts = filespec.split(":")
        url = None
        if len(parts) == 3:  # module:id:attachment
            if parts[0] in ["wiki", "ticket"]:
                module, id, file = parts
            else:
                raise Exception(u"Le module %s ne peut pas posséder de fichiers " "joints" % parts[0])
        elif len(parts) == 2:
            from trac.versioncontrol.web_ui import BrowserModule

            try:
                browser_links = [link for link, _ in BrowserModule(self.env).get_link_resolvers()]
            except Exception:
                browser_links = []
            if parts[0] in browser_links:  # source:path
                module, file = parts
                rev = None
                if "@" in file:
                    file, rev = file.split("@")
                url = req.href.browser(file, rev=rev)
                raw_url = req.href.browser(file, rev=rev, format="raw")
                desc = filespec
            else:  # #ticket:attachment or WikiPage:attachment
                # FIXME: do something generic about shorthand forms...
                id, file = parts
                if id and id[0] == "#":
                    module = "ticket"
                    id = id[1:]
                elif id == "htdocs":
                    raw_url = url = req.href.chrome("site", file)
                    desc = os.path.basename(file)
                elif id in ("http", "https", "ftp"):  # external URLs
                    raw_url = url = desc = id + ":" + file
                else:
                    module = "wiki"
        elif len(parts) == 1:  # attachment
            # determine current object
            # FIXME: should be retrieved from the formatter...
            # ...and the formatter should be provided to the macro
            file = filespec
            module, id = "wiki", "WikiStart"
            path_info = req.path_info.split("/", 2)
            if len(path_info) > 1:
                module = path_info[1]
            if len(path_info) > 2:
                id = path_info[2]
            if module not in ["wiki", "ticket"]:
                raise Exception(u"Impossible de référencer un fichier joint " u"depuis cet endroit")
        else:
            raise Exception(u"Pas de définition de fichier reçue")
        if not url:  # this is an attachment
            from trac.attachment import Attachment

            attachment = Attachment(self.env, module, id, file)
            url = attachment.href(req)
            raw_url = attachment.href(req, format="raw")
            desc = attachment.description
        for key in ["title", "alt"]:
            if desc and not attr.has_key(key):
                attr[key] = desc
        if style:
            attr["style"] = "; ".join(["%s:%s" % (k, escape(v)) for k, v in style.iteritems()])
        result = Markup(html.IMG(src=raw_url, **attr)).sanitize()
        if not nolink:
            result = html.A(result, href=url, style="padding:0; border:none")
        return result
Beispiel #37
0
    def _diff_to_hdf(self, difflines, tabwidth):
        """
        Translate a diff file into something suitable for inclusion in HDF.
        The result is [(filename, revname_old, revname_new, changes)],
        where changes has the same format as the result of
        `trac.versioncontrol.diff.hdf_diff`.

        If the diff cannot be parsed, this method returns None.
        """
        def _markup_intraline_change(fromlines, tolines):
            from trac.versioncontrol.diff import get_change_extent
            for i in xrange(len(fromlines)):
                fr, to = fromlines[i], tolines[i]
                (start, end) = get_change_extent(fr, to)
                if start != 0 or end != 0:
                    last = end + len(fr)
                    fromlines[i] = fr[:start] + '\0' + fr[start:last] + \
                                   '\1' + fr[last:]
                    last = end + len(to)
                    tolines[i] = to[:start] + '\0' + to[start:last] + \
                                 '\1' + to[last:]

        space_re = re.compile(' ( +)|^ ')

        def htmlify(match):
            div, mod = divmod(len(match.group(0)), 2)
            return Markup(div * '&nbsp; ' + mod * '&nbsp;')

        comments = []
        changes = []
        lines = iter(difflines)
        try:
            line = next(lines)
            while True:
                oldpath = oldrev = newpath = newrev = ''
                oldinfo = newinfo = []
                binary = False

                # consume preamble, storing free lines in comments
                # (also detect the special case of git binary patches)
                if not line.startswith('--- '):
                    if not line.startswith('Index: ') and line != '=' * 67:
                        comments.append(line)
                    if line == "GIT binary patch":
                        binary = True
                        diffcmd_line = comments[0]  # diff --git a/... b/,,,
                        oldpath, newpath = diffcmd_line.split()[-2:]
                        if any(c.startswith('new file') for c in comments):
                            oldpath = '/dev/null'
                        if any(c.startswith('deleted file') for c in comments):
                            newpath = '/dev/null'
                        oldinfo = ['', oldpath]
                        newinfo = ['', newpath]
                        index = [c for c in comments if c.startswith('index ')]
                        if index:  # index 8f****78..1e****5c
                            oldrev, newrev = index[0].split()[-1].split('..')
                            oldinfo.append(oldrev)
                            newinfo.append(newrev)
                        line = next(lines)
                        while line:
                            comments.append(line)
                            line = next(lines)
                    else:
                        line = next(lines)
                        continue

                if not oldinfo and not newinfo:
                    # Base filename/version from '--- <file> [rev]'
                    oldinfo = line.split(None, 2)
                    if len(oldinfo) > 1:
                        oldpath = oldinfo[1]
                        if len(oldinfo) > 2:
                            oldrev = oldinfo[2]

                    # Changed filename/version from '+++ <file> [rev]'
                    line = next(lines)
                    if not line.startswith('+++ '):
                        self.log.debug('expected +++ after ---, got %s', line)
                        return None

                    newinfo = line.split(None, 2)
                    if len(newinfo) > 1:
                        newpath = newinfo[1]
                        if len(newinfo) > 2:
                            newrev = newinfo[2]

                shortrev = ('old', 'new')
                if oldpath or newpath:
                    sep = re.compile(r'([/.~\\])')
                    commonprefix = ''.join(
                        os.path.commonprefix(
                            [sep.split(newpath),
                             sep.split(oldpath)]))
                    commonsuffix = ''.join(
                        os.path.commonprefix([
                            sep.split(newpath)[::-1],
                            sep.split(oldpath)[::-1]
                        ])[::-1])
                    if len(commonprefix) > len(commonsuffix):
                        common = commonprefix
                    elif commonsuffix:
                        common = commonsuffix.lstrip('/')
                        a = oldpath[:-len(commonsuffix)]
                        b = newpath[:-len(commonsuffix)]
                        if len(a) < 4 and len(b) < 4:
                            shortrev = (a, b)
                    elif oldpath == '/dev/null':
                        common = _("new file %(new)s",
                                   new=newpath.lstrip('b/'))
                        shortrev = ('-', '+')
                    elif newpath == '/dev/null':
                        common = _("deleted file %(deleted)s",
                                   deleted=oldpath.lstrip('a/'))
                        shortrev = ('+', '-')
                    else:
                        common = '(a) %s vs. (b) %s' % (oldpath, newpath)
                        shortrev = ('a', 'b')
                else:
                    common = ''

                groups = []
                groups_title = []
                changes.append({
                    'change': 'edit',
                    'props': [],
                    'comments': '\n'.join(comments),
                    'binary': binary,
                    'diffs': groups,
                    'diffs_title': groups_title,
                    'old': {
                        'path': common,
                        'rev': ' '.join(oldinfo[1:]),
                        'shortrev': shortrev[0]
                    },
                    'new': {
                        'path': common,
                        'rev': ' '.join(newinfo[1:]),
                        'shortrev': shortrev[1]
                    }
                })
                comments = []
                line = next(lines)
                while line:
                    # "@@ -333,10 +329,8 @@" or "@@ -1 +1 @@ [... title ...]"
                    r = re.match(
                        r'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@'
                        '(.*)', line)
                    if not r:
                        break
                    blocks = []
                    groups.append(blocks)
                    fromline, fromend, toline, toend = \
                            [int(x or 1) for x in r.groups()[:4]]
                    groups_title.append(r.group(5))
                    last_type = extra = None

                    fromend += fromline
                    toend += toline
                    line = next(lines)
                    while fromline < fromend or toline < toend or extra:

                        # First character is the command
                        command = ' '
                        if line:
                            command, line = line[0], line[1:]
                        # Make a new block?
                        if (command == ' ') != last_type:
                            last_type = command == ' '
                            kind = 'unmod' if last_type else 'mod'
                            block = {
                                'type': kind,
                                'base': {
                                    'offset': fromline - 1,
                                    'lines': []
                                },
                                'changed': {
                                    'offset': toline - 1,
                                    'lines': []
                                }
                            }
                            blocks.append(block)
                        else:
                            block = blocks[-1]
                        if command == ' ':
                            sides = ['base', 'changed']
                        elif command == '+':
                            last_side = 'changed'
                            sides = [last_side]
                        elif command == '-':
                            last_side = 'base'
                            sides = [last_side]
                        elif command == '\\' and last_side:
                            meta = block[last_side].setdefault('meta', {})
                            meta[len(block[last_side]['lines'])] = True
                            sides = [last_side]
                        elif command == '@':  # ill-formed patch
                            groups_title[-1] = "%s (%s)" % (
                                groups_title[-1],
                                _("this hunk was shorter than expected"))
                            line = '@' + line
                            break
                        else:
                            self.log.debug('expected +, - or \\, got %s',
                                           command)
                            return None
                        for side in sides:
                            if side == 'base':
                                fromline += 1
                            else:
                                toline += 1
                            block[side]['lines'].append(line)
                        line = next(lines)
                        extra = line and line[0] == '\\'
        except StopIteration:
            pass

        # Go through all groups/blocks and mark up intraline changes, and
        # convert to html
        for o in changes:
            for group in o['diffs']:
                for b in group:
                    base, changed = b['base'], b['changed']
                    f, t = base['lines'], changed['lines']
                    if b['type'] == 'mod':
                        if len(f) == 0:
                            b['type'] = 'add'
                        elif len(t) == 0:
                            b['type'] = 'rem'
                        elif len(f) == len(t):
                            _markup_intraline_change(f, t)
                    for i in xrange(len(f)):
                        line = expandtabs(f[i], tabwidth, '\0\1')
                        line = escape(line, quotes=False)
                        line = '<del>'.join(
                            space_re.sub(htmlify, seg)
                            for seg in line.split('\0'))
                        line = line.replace('\1', '</del>')
                        f[i] = Markup(line)
                        if 'meta' in base and i in base['meta']:
                            f[i] = Markup('<em>%s</em>') % f[i]
                    for i in xrange(len(t)):
                        line = expandtabs(t[i], tabwidth, '\0\1')
                        line = escape(line, quotes=False)
                        line = '<ins>'.join(
                            space_re.sub(htmlify, seg)
                            for seg in line.split('\0'))
                        line = line.replace('\1', '</ins>')
                        t[i] = Markup(line)
                        if 'meta' in changed and i in changed['meta']:
                            t[i] = Markup('<em>%s</em>') % t[i]
        return changes
Beispiel #38
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 #39
0
 def test_escape(self):
     markup = escape('<b>"&"</b>')
     assert isinstance(markup, Markup)
     self.assertEquals('&lt;b&gt;&#34;&amp;&#34;&lt;/b&gt;', markup)
Beispiel #40
0
 def wrap_key(key):
     return '<p style="word-wrap: break-word; margin: 1em 0">{0}</p>'.format(
             escape(key))
Beispiel #41
0
    def expand_macro(self, formatter, name, content):
        # args will be null if the macro is called without parenthesis.
        if not content:
            return ''
        # parse arguments
        # we expect the 1st argument to be a filename (filespec)
        args = content.split(',')
        if len(args) == 0:
            raise Exception("No argument.")
        # strip unicode white-spaces and ZWSPs are copied from attachments
        # section (#10668)
        filespec = stripws(args.pop(0))

        # style information
        size_re = re.compile('[0-9]+(%|px)?$')
        attr_re = re.compile('(align|valign|border|width|height|alt'
                             '|margin(?:-(?:left|right|top|bottom))?'
                             '|title|longdesc|class|id|usemap)=(.+)')
        quoted_re = re.compile("(?:[\"'])(.*)(?:[\"'])$")
        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 = stripws(args.pop(0))
            if 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 = 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' % int(val)
                    elif key in ('margin', 'margin-left', 'margin-right') \
                             and 'display' not in style:
                        style[key] = ' %dpx' % int(val)
                    elif key == 'border':
                        style['border'] = ' %dpx solid' % int(val)
                    else:
                        m = quoted_re.search(val)  # unquote "..." and '...'
                        if m:
                            val = m.group(1)
                        attr[str(key)] = val  # will be used as a __call__ kwd

        # parse filespec argument to get realm and id if contained.
        parts = [
            i.strip('''['"]''')
            for i in self._split_filespec_re.split(filespec)
        ]
        url = raw_url = desc = None
        attachment = None
        if (parts and parts[0] in ('http', 'https', 'ftp')):  # absolute
            raw_url = url = filespec
            desc = url.rsplit('?')[0]
        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:
            raise TracError('No filespec given')
        if attachment and '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')
            try:
                desc = get_resource_summary(self.env, attachment)
            except ResourceNotFound as e:
                raw_url = formatter.href.chrome('common/attachment.png')
                desc = _('No image "%(id)s" attached to %(parent)s',
                         id=attachment.id,
                         parent=get_resource_name(self.env, attachment.parent))
        for key in ('title', 'alt'):
            if desc and not key in attr:
                attr[key] = desc
        if style:
            attr['style'] = '; '.join('%s:%s' % (k, escape(v))
                                      for k, v in style.iteritems())
        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 #42
0
    def _diff_to_hdf(self, difflines, tabwidth):
        """
        Translate a diff file into something suitable for inclusion in HDF.
        The result is [(filename, revname_old, revname_new, changes)],
        where changes has the same format as the result of
        `trac.versioncontrol.diff.hdf_diff`.

        If the diff cannot be parsed, this method returns None.
        """
        def _markup_intraline_change(fromlines, tolines):
            from trac.versioncontrol.diff import _get_change_extent
            for i in xrange(len(fromlines)):
                fr, to = fromlines[i], tolines[i]
                (start, end) = _get_change_extent(fr, to)
                if start != 0 or end != 0:
                    last = end+len(fr)
                    fromlines[i] = fr[:start] + '\0' + fr[start:last] + \
                                   '\1' + fr[last:]
                    last = end+len(to)
                    tolines[i] = to[:start] + '\0' + to[start:last] + \
                                 '\1' + to[last:]

        import re
        space_re = re.compile(' ( +)|^ ')
        def htmlify(match):
            div, mod = divmod(len(match.group(0)), 2)
            return div * '&nbsp; ' + mod * '&nbsp;'

        output = []
        filename, groups = None, None
        lines = iter(difflines)
        try:
            line = lines.next()
            while True:
                if not line.startswith('--- '):
                    line = lines.next()
                    continue

                # Base filename/version
                words = line.split(None, 2)
                filename, fromrev = words[1], 'old'
                groups, blocks = None, None

                # Changed filename/version
                line = lines.next()
                if not line.startswith('+++ '):
                    return None

                words = line.split(None, 2)
                if len(words[1]) < len(filename):
                    # Always use the shortest filename for display
                    filename = words[1]
                groups = []
                output.append({'filename' : filename, 'oldrev' : fromrev,
                               'newrev' : 'new', 'diff' : groups})

                for line in lines:
                    # @@ -333,10 +329,8 @@
                    r = re.match(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@', line)
                    if not r:
                        break
                    blocks = []
                    groups.append(blocks)
                    fromline,fromend,toline,toend = map(int, r.groups())
                    last_type = None

                    fromend += fromline
                    toend += toline

                    while fromline < fromend or toline < toend:
                        line = lines.next()

                        # First character is the command
                        command = ' '
                        if line:
                            command, line = line[0], line[1:]
                        # Make a new block?
                        if (command == ' ') != last_type:
                            last_type = command == ' '
                            blocks.append({'type': last_type and 'unmod' or 'mod',
                                           'base.offset': fromline - 1,
                                           'base.lines': [],
                                           'changed.offset': toline - 1,
                                           'changed.lines': []})
                        if command == ' ':
                            blocks[-1]['changed.lines'].append(line)
                            blocks[-1]['base.lines'].append(line)
                            fromline += 1
                            toline += 1
                        elif command == '+':
                            blocks[-1]['changed.lines'].append(line)
                            toline += 1
                        elif command == '-':
                            blocks[-1]['base.lines'].append(line)
                            fromline += 1
                        else:
                            return None
                line = lines.next()
        except StopIteration:
            pass

        # Go through all groups/blocks and mark up intraline changes, and
        # convert to html
        for o in output:
            for group in o['diff']:
                for b in group:
                    f, t = b['base.lines'], b['changed.lines']
                    if b['type'] == 'mod':
                        if len(f) == 0:
                            b['type'] = 'add'
                        elif len(t) == 0:
                            b['type'] = 'rem'
                        elif len(f) == len(t):
                            _markup_intraline_change(f, t)
                    for i in xrange(len(f)):
                        line = f[i].expandtabs(tabwidth)
                        line = escape(line)
                        line = '<del>'.join([space_re.sub(htmlify, seg)
                                             for seg in line.split('\0')])
                        line = line.replace('\1', '</del>')
                        f[i] = Markup(line)
                    for i in xrange(len(t)):
                        line = t[i].expandtabs(tabwidth)
                        line = escape(line)
                        line = '<ins>'.join([space_re.sub(htmlify, seg)
                                             for seg in line.split('\0')])
                        line = line.replace('\1', '</ins>')
                        t[i] = Markup(line)
        return output
Beispiel #43
0
def _save_messages(req, url, permanent):
    """Save warnings and notices in case of redirect, so that they can
    be displayed after the redirect."""
    for type_ in ['warnings', 'notices']:
        for (i, message) in enumerate(req.chrome[type_]):
            req.session['chrome.%s.%d' % (type_, i)] = escape(message)
Beispiel #44
0
def diff_blocks(fromlines, tolines, context=None, tabwidth=8,
                ignore_blank_lines=0, ignore_case=0, ignore_space_changes=0):
    """Return an array that is adequate for adding to the data dictionary

    See the diff_div.html template.
    """

    type_map = {'replace': 'mod', 'delete': 'rem', 'insert': 'add',
                'equal': 'unmod'}

    space_re = re.compile(' ( +)|^ ')
    def htmlify(match):
        div, mod = divmod(len(match.group(0)), 2)
        return div * '&nbsp; ' + mod * '&nbsp;'

    def markup_intraline_changes(opcodes):
        for tag, i1, i2, j1, j2 in opcodes:
            if tag == 'replace' and i2 - i1 == j2 - j1:
                for i in range(i2 - i1):
                    fromline, toline = fromlines[i1 + i], tolines[j1 + i]
                    (start, end) = _get_change_extent(fromline, toline)
                    if start != 0 or end != 0:
                        last = end+len(fromline)
                        fromlines[i1+i] = fromline[:start] + '\0' + fromline[start:last] + \
                                       '\1' + fromline[last:]
                        last = end+len(toline)
                        tolines[j1+i] = toline[:start] + '\0' + toline[start:last] + \
                                     '\1' + toline[last:]
            yield tag, i1, i2, j1, j2

    changes = []
    opcodes = _get_opcodes(fromlines, tolines, ignore_blank_lines, ignore_case,
                           ignore_space_changes)
    for group in _group_opcodes(opcodes, context):
        blocks = []
        last_tag = None
        for tag, i1, i2, j1, j2 in markup_intraline_changes(group):
            if tag != last_tag:
                blocks.append({'type': type_map[tag],
                               'base': {'offset': i1, 'lines': []},
                               'changed': {'offset': j1, 'lines': []}})
            if tag == 'equal':
                for line in fromlines[i1:i2]:
                    line = line.expandtabs(tabwidth)
                    line = space_re.sub(htmlify, escape(line, quotes=False))
                    blocks[-1]['base']['lines'].append(Markup(unicode(line)))
                for line in tolines[j1:j2]:
                    line = line.expandtabs(tabwidth)
                    line = space_re.sub(htmlify, escape(line, quotes=False))
                    blocks[-1]['changed']['lines'].append(Markup(unicode(line)))
            else:
                if tag in ('replace', 'delete'):
                    for line in fromlines[i1:i2]:
                        line = expandtabs(line, tabwidth, '\0\1')
                        line = escape(line, quotes=False)
                        line = '<del>'.join([space_re.sub(htmlify, seg)
                                             for seg in line.split('\0')])
                        line = line.replace('\1', '</del>')
                        blocks[-1]['base']['lines'].append(
                            Markup(unicode(line)))
                if tag in ('replace', 'insert'):
                    for line in tolines[j1:j2]:
                        line = expandtabs(line, tabwidth, '\0\1')
                        line = escape(line, quotes=False)
                        line = '<ins>'.join([space_re.sub(htmlify, seg)
                                             for seg in line.split('\0')])
                        line = line.replace('\1', '</ins>')
                        blocks[-1]['changed']['lines'].append(
                            Markup(unicode(line)))
        changes.append(blocks)
    return changes
Beispiel #45
0
                               (png_file_size, png_file_time, description, png_attachment.author, png_attachment.ipnr,
                                png_attachment.parent_realm, unicode(png_attachment.parent_id), png_filename))
                self.env.log.info('Updated attachment: %s by %s', png_filename, png_attachment.author)
            else:
                # Insert as new entry
                cursor.execute("INSERT INTO attachment VALUES (%s,%s,%s,%s,%s,%s,%s,%s)",
                        (png_attachment.parent_realm, png_attachment.parent_id, png_filename,
                            png_file_size, png_file_time, description,
                            png_attachment.author, png_attachment.ipnr))
                self.env.log.info('New attachment: %s by %s', png_filename, png_attachment.author)

            db.commit()
            cursor.close()
            # This has been included in the hope it would help update
            # the current page being displayed, but no effect noticed
            for listener in AttachmentModule(self.env).change_listeners:
                listener.attachment_added(self)

        for key in ('title', 'alt'):
            if not key in attr:
                attr[key] = description
        if style:
            attr['style'] = '; '.join(['%s:%s' % (k, escape(v))
                                       for k, v in style.iteritems()])
        result = tag.img(src=png_url + "?format=raw", **attr)
        if link is not None:
            result = tag.p(result, href=link or url,
                           style='padding:2; border:none')

        return result
Beispiel #46
0
    def expand_macro(self, formatter, name, content):
        # args will be null if the macro is called without parenthesis.
        if not content:
            return ''
        # parse arguments
        # we expect the 1st argument to be a filename (filespec)
        args = content.split(',')
        if len(args) == 0:
            raise Exception("No argument.")
        filespec = args[0]

        # style information
        size_re = re.compile('[0-9]+(%|px)?$')
        attr_re = re.compile('(align|border|width|height|alt'
                             '|title|longdesc|class|id|usemap)=(.+)')
        quoted_re = re.compile("(?:[\"'])(.*)(?:[\"'])$")
        attr = {}
        style = {}
        link = ''
        for arg in args[1:]:
            arg = arg.strip()
            if size_re.match(arg):
                # 'width' keyword
                attr['width'] = arg
                continue
            if arg == 'nolink':
                link = None
                continue
            if arg.startswith('link='):
                val = arg.split('=', 1)[1]
                elt = extract_link(self.env, formatter.context, val.strip())
                link = None
                if isinstance(elt, Element):
                    link = elt.attrib.get('href')
                continue
            if arg in ('left', 'right', 'top', 'bottom'):
                style['float'] = arg
                continue
            match = attr_re.match(arg)
            if match:
                key, val = match.groups()
                m = quoted_re.search(val) # unquote "..." and '...'
                if m:
                    val = m.group(1)
                if key == 'align':
                    style['float'] = val
                elif key == 'border':
                    style['border'] = ' %dpx solid' % int(val);
                else:
                    attr[str(key)] = val # will be used as a __call__ keyword

        # parse filespec argument to get realm and id if contained.
        parts = filespec.split(':')
        url = raw_url = desc = None
        attachment = None
        if (parts and parts[0] in ('http', 'https', 'ftp')): # absolute
            raw_url = url = desc = filespec
        elif filespec.startswith('//'):       # server-relative
            raw_url = url = desc = filespec[1:]
        elif filespec.startswith('/'):        # project-relative
            # use href, but unquote to allow args (use default html escaping)
            raw_url = url = desc = unquote(formatter.href(filespec))
        elif len(parts) == 3:                 # realm:id:attachment-filename
            realm, id, filename = parts
            attachment = Resource(realm, id).child('attachment', filename)
        elif len(parts) == 2:
            # FIXME: somehow use ResourceSystem.get_known_realms()
            #        ... or directly trac.wiki.extract_link
            from trac.versioncontrol.web_ui import BrowserModule
            try:
                browser_links = [res[0] for res in
                                 BrowserModule(self.env).get_link_resolvers()]
            except Exception:
                browser_links = []
            if parts[0] in browser_links:   # source:path
                # TODO: use context here as well
                realm, filename = parts
                rev = None
                if '@' in filename:
                    filename, rev = filename.split('@')
                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)
                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:
            raise TracError('No filespec given')
        if attachment and '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')
            desc = get_resource_summary(self.env, attachment)
        for key in ('title', 'alt'):
            if desc and not key in attr:
                attr[key] = desc
        if style:
            attr['style'] = '; '.join(['%s:%s' % (k, escape(v))
                                       for k, v in style.iteritems()])
        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 #47
0
 def _inlinecode2_formatter(self, match, fullmatch):
     return '<text:span text:style-name="%s">%s</text:span>' % (
         self.get_style('inline'), escape(fullmatch.group('inline2')))
Beispiel #48
0
    def render_macro(self, req, name, content):
        # args will be null if the macro is called without parenthesis.
        if not content:
            return ''
        # parse arguments
        # we expect the 1st argument to be a filename (filespec)
        args = content.split(',')
        if len(args) == 0:
            raise Exception("No argument.")
        filespec = args[0]
        size_re = re.compile('[0-9]+%?$')
        attr_re = re.compile('(align|border|width|height|alt'
                             '|title|longdesc|class|id|usemap)=(.+)')
        quoted_re = re.compile("(?:[\"'])(.*)(?:[\"'])$")
        attr = {}
        style = {}
        nolink = False
        for arg in args[1:]:
            arg = arg.strip()
            if size_re.match(arg):
                # 'width' keyword
                attr['width'] = arg
                continue
            if arg == 'nolink':
                nolink = True
                continue
            if arg in ('left', 'right', 'top', 'bottom'):
                style['float'] = arg
                continue
            match = attr_re.match(arg)
            if match:
                key, val = match.groups()
                m = quoted_re.search(val) # unquote "..." and '...'
                if m:
                    val = m.group(1)
                if key == 'align':
                    style['float'] = val
                elif key == 'border':
                    style['border'] = ' %dpx solid' % int(val);
                else:
                    attr[str(key)] = val # will be used as a __call__ keyword

        # parse filespec argument to get module and id if contained.
        parts = filespec.split(':')
        url = None
        if len(parts) == 3:                 # module:id:attachment
            if parts[0] in ['wiki', 'ticket']:
                module, id, file = parts
            else:
                raise Exception("%s module can't have attachments" % parts[0])
        elif len(parts) == 2:
            from trac.versioncontrol.web_ui import BrowserModule
            try:
                browser_links = [link for link,_ in 
                                 BrowserModule(self.env).get_link_resolvers()]
            except Exception:
                browser_links = []
            if parts[0] in browser_links:   # source:path
                module, file = parts
                rev = None
                if '@' in file:
                    file, rev = file.split('@')
                url = req.href.browser(file, rev=rev)
                raw_url = req.href.browser(file, rev=rev, format='raw')
                desc = filespec
            else: # #ticket:attachment or WikiPage:attachment
                # FIXME: do something generic about shorthand forms...
                id, file = parts
                if id and id[0] == '#':
                    module = 'ticket'
                    id = id[1:]
                elif id == 'htdocs':
                    raw_url = url = req.href.chrome('site', file)
                    desc = os.path.basename(file)
                elif id in ('http', 'https', 'ftp'): # external URLs
                    raw_url = url = desc = id+':'+file
                else:
                    module = 'wiki'
        elif len(parts) == 1:               # attachment
            # determine current object
            # FIXME: should be retrieved from the formatter...
            # ...and the formatter should be provided to the macro
            file = filespec
            module, id = 'wiki', 'WikiStart'
            path_info = req.path_info.split('/',2)
            if len(path_info) > 1:
                module = path_info[1]
            if len(path_info) > 2:
                id = path_info[2]
            if module not in ['wiki', 'ticket']:
                raise Exception('Cannot reference local attachment from here')
        else:
            raise Exception('No filespec given')
        if not url: # this is an attachment
            from trac.attachment import Attachment
            attachment = Attachment(self.env, module, id, file)
            url = attachment.href(req)
            raw_url = attachment.href(req, format='raw')
            desc = attachment.description
        for key in ['title', 'alt']:
            if desc and not attr.has_key(key):
                attr[key] = desc
        if style:
            attr['style'] = '; '.join(['%s:%s' % (k, escape(v))
                                       for k, v in style.iteritems()])
        result = Markup(html.IMG(src=raw_url, **attr)).sanitize()
        if not nolink:
            result = html.A(result, href=url, style='padding:0; border:none')
        return result
Beispiel #49
0
def _save_messages(req, url, permanent):
    """Save warnings and notices in case of redirect, so that they can
    be displayed after the redirect."""
    for type_ in ['warnings', 'notices']:
        for (i, message) in enumerate(req.chrome[type_]):
            req.session['chrome.%s.%d' % (type_, i)] = escape(message, False)
Beispiel #50
0
 def _format_syntax(self, formatter, ns, match):
     page = match.group('relwiki')
     return html.A(escape(page), href=formatter.href.wiki(self.pagebase.rstrip('/'),page), class_='wiki')
Beispiel #51
0
def hdf_diff(fromlines, tolines, context=None, tabwidth=8,
             ignore_blank_lines=0, ignore_case=0, ignore_space_changes=0):
    """
    Return an array that is adequate for adding the the HDF data set for HTML
    rendering of the differences.
    """

    type_map = {'replace': 'mod', 'delete': 'rem', 'insert': 'add',
                'equal': 'unmod'}

    space_re = re.compile(' ( +)|^ ')
    def htmlify(match):
        div, mod = divmod(len(match.group(0)), 2)
        return div * '&nbsp; ' + mod * '&nbsp;'

    def markup_intraline_changes(opcodes):
        for tag, i1, i2, j1, j2 in opcodes:
            if tag == 'replace' and i2 - i1 == j2 - j1:
                for i in range(i2 - i1):
                    fromline, toline = fromlines[i1 + i], tolines[j1 + i]
                    (start, end) = _get_change_extent(fromline, toline)

                    if start == 0 and end < 0:
                        # Change at start of line
                        fromlines[i1 + i] = '\0' + fromline[:end] + '\1' + \
                                            fromline[end:]
                        tolines[j1 + i] = '\0' + toline[:end] + '\1' + \
                                          toline[end:]
                    elif start > 0 and end == 0:
                        # Change at end of line
                        fromlines[i1 + i] = fromline[:start] + '\0' + \
                                            fromline[start:] + '\1'
                        tolines[j1 + i] = toline[:start] + '\0' + \
                                          toline[start:] + '\1'
                    elif start > 0 and end < 0:
                        # Change somewhere in the middle
                        fromlines[i1 + i] = fromline[:start] + '\0' + \
                                            fromline[start:end] + '\1' + \
                                            fromline[end:]
                        tolines[j1 + i] = toline[:start] + '\0' + \
                                          toline[start:end] + '\1' + \
                                          toline[end:]
            yield tag, i1, i2, j1, j2

    changes = []
    opcodes = _get_opcodes(fromlines, tolines, ignore_blank_lines, ignore_case,
                           ignore_space_changes)
    for group in _group_opcodes(opcodes, context):
        blocks = []
        last_tag = None
        for tag, i1, i2, j1, j2 in markup_intraline_changes(group):
            if tag != last_tag:
                blocks.append({'type': type_map[tag], 'base.offset': i1,
                               'base.lines': [], 'changed.offset': j1,
                               'changed.lines': []})
            if tag == 'equal':
                for line in fromlines[i1:i2]:
                    line = line.expandtabs(tabwidth)
                    line = space_re.sub(htmlify, escape(line, quotes=False))
                    blocks[-1]['base.lines'].append(Markup(line))
                for line in tolines[j1:j2]:
                    line = line.expandtabs(tabwidth)
                    line = space_re.sub(htmlify, escape(line, quotes=False))
                    blocks[-1]['changed.lines'].append(Markup(line))
            else:
                if tag in ('replace', 'delete'):
                    for line in fromlines[i1:i2]:
                        line = line.expandtabs(tabwidth)
                        line = escape(line, quotes=False).replace('\0', '<del>') \
                                                         .replace('\1', '</del>')
                        blocks[-1]['base.lines'].append(Markup(space_re.sub(htmlify,
                                                                            line)))
                if tag in ('replace', 'insert'):
                    for line in tolines[j1:j2]:
                        line = line.expandtabs(tabwidth)
                        line = escape(line, quotes=False).replace('\0', '<ins>') \
                                                         .replace('\1', '</ins>')
                        blocks[-1]['changed.lines'].append(Markup(space_re.sub(htmlify,
                                                                               line)))
        changes.append(blocks)
    return changes
Beispiel #52
0
    def _diff_to_hdf(self, difflines, tabwidth):
        """
        Translate a diff file into something suitable for inclusion in HDF.
        The result is [(filename, revname_old, revname_new, changes)],
        where changes has the same format as the result of
        `trac.versioncontrol.diff.hdf_diff`.

        If the diff cannot be parsed, this method returns None.
        """
        def _markup_intraline_change(fromlines, tolines):
            from trac.versioncontrol.diff import _get_change_extent
            for i in xrange(len(fromlines)):
                fr, to = fromlines[i], tolines[i]
                (start, end) = _get_change_extent(fr, to)
                if start != 0 or end != 0:
                    last = end+len(fr)
                    fromlines[i] = fr[:start] + '\0' + fr[start:last] + \
                                   '\1' + fr[last:]
                    last = end+len(to)
                    tolines[i] = to[:start] + '\0' + to[start:last] + \
                                 '\1' + to[last:]

        import re
        space_re = re.compile(' ( +)|^ ')
        def htmlify(match):
            div, mod = divmod(len(match.group(0)), 2)
            return div * '&nbsp; ' + mod * '&nbsp;'

        comments = []
        changes = []
        lines = iter(difflines)
        try:
            line = lines.next()
            while True:
                if not line.startswith('--- '):
                    if not line.startswith('Index: ') and line != '='*67:
                        comments.append(line)
                    line = lines.next()
                    continue

                oldpath = oldrev = newpath = newrev = ''

                # Base filename/version
                oldinfo = line.split(None, 2)
                if len(oldinfo) > 1:
                    oldpath = oldinfo[1]
                    if len(oldinfo) > 2:
                        oldrev = oldinfo[2]

                # Changed filename/version
                line = lines.next()
                if not line.startswith('+++ '):
                    self.log.debug('expected +++ after ---, got '+line)
                    return None

                newinfo = line.split(None, 2)
                if len(newinfo) > 1:
                    newpath = newinfo[1]
                    if len(newinfo) > 2:
                        newrev = newinfo[2]

                shortrev = ('old', 'new')
                if oldpath or newpath:
                    sep = re.compile(r'([/.~\\])')
                    commonprefix = ''.join(os.path.commonprefix(
                        [sep.split(newpath), sep.split(oldpath)]))
                    commonsuffix = ''.join(os.path.commonprefix(
                        [sep.split(newpath)[::-1],
                         sep.split(oldpath)[::-1]])[::-1])
                    if len(commonprefix) > len(commonsuffix):
                        common = commonprefix
                    elif commonsuffix:
                        common = commonsuffix.lstrip('/')
                        a = oldpath[:-len(commonsuffix)]
                        b = newpath[:-len(commonsuffix)]
                        if len(a) < 4 and len(b) < 4:
                            shortrev = (a, b)
                    else:
                        common = '(a) %s vs. (b) %s' % (oldpath, newpath)
                        shortrev = ('a', 'b')
                else:
                    common = ''

                groups = []
                changes.append({'change': 'edit', 'props': [],
                                'comments': '\n'.join(comments),
                                'diffs': groups,
                                'old': {'path': common,
                                        'rev': ' '.join(oldinfo[1:]),
                                        'shortrev': shortrev[0]},
                                'new': {'path': common,
                                        'rev': ' '.join(newinfo[1:]),
                                        'shortrev': shortrev[1]}})
                comments = []
                line = lines.next()
                while line:
                    # "@@ -333,10 +329,8 @@" or "@@ -1 +1 @@"
                    r = re.match(r'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@',
                                 line)
                    if not r:
                        break
                    blocks = []
                    groups.append(blocks)
                    fromline, fromend, toline, toend = [int(x or 1)
                                                        for x in r.groups()]
                    last_type = last_change = extra = None

                    fromend += fromline
                    toend += toline
                    line = lines.next()
                    while fromline < fromend or toline < toend or extra:

                        # First character is the command
                        command = ' '
                        if line:
                            command, line = line[0], line[1:]
                        # Make a new block?
                        if (command == ' ') != last_type:
                            last_type = command == ' '
                            kind = last_type and 'unmod' or 'mod'
                            block = {'type': kind,
                                     'base': {'offset': fromline - 1,
                                              'lines': []},
                                     'changed': {'offset': toline - 1,
                                                 'lines': []}}
                            blocks.append(block)
                        else:
                            block = blocks[-1]
                        if command == ' ':
                            sides = ['base', 'changed']
                        elif command == '+':
                            last_side = 'changed'
                            sides = [last_side]
                        elif command == '-':
                            last_side = 'base'
                            sides = [last_side]
                        elif command == '\\' and last_side:
                            meta = block[last_side].setdefault('meta', {})
                            meta[len(block[last_side]['lines'])] = True
                            sides = [last_side]
                        else:
                            self.log.debug('expected +, - or \\, got '+command)
                            return None
                        for side in sides:
                            if side == 'base':
                                fromline += 1
                            else:
                                toline += 1
                            block[side]['lines'].append(line)
                        line = lines.next()
                        extra = line and line[0] == '\\'
        except StopIteration:
            pass

        # Go through all groups/blocks and mark up intraline changes, and
        # convert to html
        for o in changes:
            for group in o['diffs']:
                for b in group:
                    base, changed = b['base'], b['changed']
                    f, t = base['lines'], changed['lines']
                    if b['type'] == 'mod':
                        if len(f) == 0:
                            b['type'] = 'add'
                        elif len(t) == 0:
                            b['type'] = 'rem'
                        elif len(f) == len(t):
                            _markup_intraline_change(f, t)
                    for i in xrange(len(f)):
                        line = expandtabs(f[i], tabwidth, '\0\1')
                        line = escape(line, quotes=False)
                        line = '<del>'.join([space_re.sub(htmlify, seg)
                                             for seg in line.split('\0')])
                        line = line.replace('\1', '</del>')
                        f[i] = Markup(line)
                        if 'meta' in base and i in base['meta']:
                            f[i] = Markup('<em>%s</em>') % f[i]
                    for i in xrange(len(t)):
                        line = expandtabs(t[i], tabwidth, '\0\1')
                        line = escape(line, quotes=False)
                        line = '<ins>'.join([space_re.sub(htmlify, seg)
                                             for seg in line.split('\0')])
                        line = line.replace('\1', '</ins>')
                        t[i] = Markup(line)
                        if 'meta' in changed and i in changed['meta']:
                            t[i] = Markup('<em>%s</em>') % t[i]
        return changes
Beispiel #53
0
 def test_escape_noquotes(self):
     markup = escape('<b>"&"</b>', quotes=False)
     assert isinstance(markup, Markup)
     self.assertEquals('&lt;b&gt;"&amp;"&lt;/b&gt;', markup)
Beispiel #54
0
 def runTest(self):
     """Admin create duplicate resolution"""
     name = "DuplicateResolution"
     self._tester.create_resolution(name)
     self._tester.create_resolution(name)
     tc.find(escape('Resolution value "%s" already exists' % name))
Beispiel #55
0
def diff_blocks(fromlines,
                tolines,
                context=None,
                tabwidth=8,
                ignore_blank_lines=0,
                ignore_case=0,
                ignore_space_changes=0):
    """Return an array that is adequate for adding to the data dictionary

    See `get_filtered_hunks` for the parameter descriptions.

    See also the diff_div.html template.
    """

    type_map = {
        'replace': 'mod',
        'delete': 'rem',
        'insert': 'add',
        'equal': 'unmod'
    }

    space_re = re.compile(' ( +)|^ ')

    def htmlify(match):
        div, mod = divmod(len(match.group(0)), 2)
        return Markup(div * '&nbsp; ' + mod * '&nbsp;')

    def markup_intraline_changes(opcodes):
        for tag, i1, i2, j1, j2 in opcodes:
            if tag == 'replace' and i2 - i1 == j2 - j1:
                for i in xrange(i2 - i1):
                    fromline, toline = fromlines[i1 + i], tolines[j1 + i]
                    (start, end) = get_change_extent(fromline, toline)
                    if start != 0 or end != 0:
                        last = end + len(fromline)
                        fromlines[i1 + i] = (fromline[:start] + '\0' +
                                             fromline[start:last] + '\1' +
                                             fromline[last:])
                        last = end + len(toline)
                        tolines[j1 + i] = (toline[:start] + '\0' +
                                           toline[start:last] + '\1' +
                                           toline[last:])
            yield tag, i1, i2, j1, j2

    changes = []
    for group in get_filtered_hunks(fromlines, tolines, context,
                                    ignore_blank_lines, ignore_case,
                                    ignore_space_changes):
        blocks = []
        last_tag = None
        for tag, i1, i2, j1, j2 in markup_intraline_changes(group):
            if tag != last_tag:
                blocks.append({
                    'type': type_map[tag],
                    'base': {
                        'offset': i1,
                        'lines': []
                    },
                    'changed': {
                        'offset': j1,
                        'lines': []
                    }
                })
            if tag == 'equal':
                for line in fromlines[i1:i2]:
                    line = line.expandtabs(tabwidth)
                    line = space_re.sub(htmlify, escape(line, quotes=False))
                    blocks[-1]['base']['lines'].append(Markup(unicode(line)))
                for line in tolines[j1:j2]:
                    line = line.expandtabs(tabwidth)
                    line = space_re.sub(htmlify, escape(line, quotes=False))
                    blocks[-1]['changed']['lines'].append(Markup(
                        unicode(line)))
            else:
                if tag in ('replace', 'delete'):
                    for line in fromlines[i1:i2]:
                        line = expandtabs(line, tabwidth, '\0\1')
                        line = escape(line, quotes=False)
                        line = '<del>'.join(
                            space_re.sub(htmlify, seg)
                            for seg in line.split('\0'))
                        line = line.replace('\1', '</del>')
                        blocks[-1]['base']['lines'].append(
                            Markup(unicode(line)))
                if tag in ('replace', 'insert'):
                    for line in tolines[j1:j2]:
                        line = expandtabs(line, tabwidth, '\0\1')
                        line = escape(line, quotes=False)
                        line = '<ins>'.join(
                            space_re.sub(htmlify, seg)
                            for seg in line.split('\0'))
                        line = line.replace('\1', '</ins>')
                        blocks[-1]['changed']['lines'].append(
                            Markup(unicode(line)))
        changes.append(blocks)
    return changes
Beispiel #56
0
 def runTest(self):
     """Admin create duplicate type"""
     name = "DuplicateType"
     self._tester.create_type(name)
     self._tester.create_type(name)
     tc.find(escape('Type value "%s" already exists' % name))
Beispiel #57
0
    def _diff_to_hdf(self, difflines, tabwidth):
        """
        Translate a diff file into something suitable for inclusion in HDF.
        The result is [(filename, revname_old, revname_new, changes)],
        where changes has the same format as the result of
        `trac.versioncontrol.diff.hdf_diff`.

        If the diff cannot be parsed, this method returns None.
        """
        def _markup_intraline_change(fromlines, tolines):
            from trac.versioncontrol.diff import get_change_extent
            for i in xrange(len(fromlines)):
                fr, to = fromlines[i], tolines[i]
                (start, end) = get_change_extent(fr, to)
                if start != 0 or end != 0:
                    last = end+len(fr)
                    fromlines[i] = fr[:start] + '\0' + fr[start:last] + \
                                   '\1' + fr[last:]
                    last = end+len(to)
                    tolines[i] = to[:start] + '\0' + to[start:last] + \
                                 '\1' + to[last:]

        import re
        space_re = re.compile(' ( +)|^ ')
        def htmlify(match):
            div, mod = divmod(len(match.group(0)), 2)
            return div * '&nbsp; ' + mod * '&nbsp;'

        comments = []
        changes = []
        lines = iter(difflines)
        try:
            line = lines.next()
            while True:
                oldpath = oldrev = newpath = newrev = ''
                oldinfo = newinfo = []
                binary = False

                # consume preample, storing free lines in comments
                # (also detect the special case of git binary patches)
                if not line.startswith('--- '):
                    if not line.startswith('Index: ') and line != '=' * 67:
                        comments.append(line)
                    if line == "GIT binary patch":
                        binary = True
                        diffcmd_line = comments[0] # diff --git a/... b/,,,
                        oldpath, newpath = diffcmd_line.split()[-2:]
                        if any(c.startswith('new file') for c in comments):
                            oldpath = '/dev/null'
                        if any(c.startswith('deleted file') for c in comments):
                            newpath = '/dev/null'
                        oldinfo = ['', oldpath]
                        newinfo = ['', newpath]
                        index = [c for c in comments if c.startswith('index ')]
                        if index: # index 8f****78..1e****5c
                            oldrev, newrev = index[0].split()[-1].split('..')
                            oldinfo.append(oldrev)
                            newinfo.append(newrev)
                        line = lines.next()
                        while line:
                            comments.append(line)
                            line = lines.next()
                    else:
                        line = lines.next()
                        continue

                if not oldinfo and not newinfo:
                    # Base filename/version from '--- <file> [rev]'
                    oldinfo = line.split(None, 2)
                    if len(oldinfo) > 1:
                        oldpath = oldinfo[1]
                        if len(oldinfo) > 2:
                            oldrev = oldinfo[2]

                    # Changed filename/version from '+++ <file> [rev]'
                    line = lines.next()
                    if not line.startswith('+++ '):
                        self.log.debug('expected +++ after ---, got ' + line)
                        return None

                    newinfo = line.split(None, 2)
                    if len(newinfo) > 1:
                        newpath = newinfo[1]
                        if len(newinfo) > 2:
                            newrev = newinfo[2]

                shortrev = ('old', 'new')
                if oldpath or newpath:
                    sep = re.compile(r'([/.~\\])')
                    commonprefix = ''.join(os.path.commonprefix(
                        [sep.split(newpath), sep.split(oldpath)]))
                    commonsuffix = ''.join(os.path.commonprefix(
                        [sep.split(newpath)[::-1],
                         sep.split(oldpath)[::-1]])[::-1])
                    if len(commonprefix) > len(commonsuffix):
                        common = commonprefix
                    elif commonsuffix:
                        common = commonsuffix.lstrip('/')
                        a = oldpath[:-len(commonsuffix)]
                        b = newpath[:-len(commonsuffix)]
                        if len(a) < 4 and len(b) < 4:
                            shortrev = (a, b)
                    elif oldpath == '/dev/null':
                        common = _("new file %(new)s",
                                   new=newpath.lstrip('b/'))
                        shortrev = ('-', '+')
                    elif newpath == '/dev/null':
                        common = _("deleted file %(deleted)s", 
                                   deleted=oldpath.lstrip('a/'))
                        shortrev = ('+', '-')
                    else:
                        common = '(a) %s vs. (b) %s' % (oldpath, newpath)
                        shortrev = ('a', 'b')
                else:
                    common = ''

                groups = []
                groups_title = []
                changes.append({'change': 'edit', 'props': [],
                                'comments': '\n'.join(comments),
                                'binary': binary,
                                'diffs': groups, 
                                'diffs_title': groups_title,
                                'old': {'path': common,
                                        'rev': ' '.join(oldinfo[1:]),
                                        'shortrev': shortrev[0]},
                                'new': {'path': common,
                                        'rev': ' '.join(newinfo[1:]),
                                        'shortrev': shortrev[1]}})
                comments = []
                line = lines.next()
                while line:
                    # "@@ -333,10 +329,8 @@" or "@@ -1 +1 @@ [... title ...]"
                    r = re.match(r'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@'
                                  '(.*)', line)
                    if not r:
                        break
                    blocks = []
                    groups.append(blocks)
                    fromline, fromend, toline, toend = \
                            [int(x or 1) for x in r.groups()[:4]]
                    groups_title.append(r.group(5))
                    last_type = extra = None

                    fromend += fromline
                    toend += toline
                    line = lines.next()
                    while fromline < fromend or toline < toend or extra:

                        # First character is the command
                        command = ' '
                        if line:
                            command, line = line[0], line[1:]
                        # Make a new block?
                        if (command == ' ') != last_type:
                            last_type = command == ' '
                            kind = 'unmod' if last_type else 'mod'
                            block = {'type': kind,
                                     'base': {'offset': fromline - 1,
                                              'lines': []},
                                     'changed': {'offset': toline - 1,
                                                 'lines': []}}
                            blocks.append(block)
                        else:
                            block = blocks[-1]
                        if command == ' ':
                            sides = ['base', 'changed']
                        elif command == '+':
                            last_side = 'changed'
                            sides = [last_side]
                        elif command == '-':
                            last_side = 'base'
                            sides = [last_side]
                        elif command == '\\' and last_side:
                            meta = block[last_side].setdefault('meta', {})
                            meta[len(block[last_side]['lines'])] = True
                            sides = [last_side]
                        elif command == '@': # ill-formed patch
                            groups_title[-1] = "%s (%s)" % (
                                groups_title[-1],
                                _("this hunk was shorter than expected"))
                            line = '@'+line
                            break
                        else:
                            self.log.debug('expected +, - or \\, got '+command)
                            return None
                        for side in sides:
                            if side == 'base':
                                fromline += 1
                            else:
                                toline += 1
                            block[side]['lines'].append(line)
                        line = lines.next()
                        extra = line and line[0] == '\\'
        except StopIteration:
            pass

        # Go through all groups/blocks and mark up intraline changes, and
        # convert to html
        for o in changes:
            for group in o['diffs']:
                for b in group:
                    base, changed = b['base'], b['changed']
                    f, t = base['lines'], changed['lines']
                    if b['type'] == 'mod':
                        if len(f) == 0:
                            b['type'] = 'add'
                        elif len(t) == 0:
                            b['type'] = 'rem'
                        elif len(f) == len(t):
                            _markup_intraline_change(f, t)
                    for i in xrange(len(f)):
                        line = expandtabs(f[i], tabwidth, '\0\1')
                        line = escape(line, quotes=False)
                        line = '<del>'.join([space_re.sub(htmlify, seg)
                                             for seg in line.split('\0')])
                        line = line.replace('\1', '</del>')
                        f[i] = Markup(line)
                        if 'meta' in base and i in base['meta']:
                            f[i] = Markup('<em>%s</em>') % f[i]
                    for i in xrange(len(t)):
                        line = expandtabs(t[i], tabwidth, '\0\1')
                        line = escape(line, quotes=False)
                        line = '<ins>'.join([space_re.sub(htmlify, seg)
                                             for seg in line.split('\0')])
                        line = line.replace('\1', '</ins>')
                        t[i] = Markup(line)
                        if 'meta' in changed and i in changed['meta']:
                            t[i] = Markup('<em>%s</em>') % t[i]
        return changes
Beispiel #58
0
 def test_escape_fragment(self):
     self.assertEqual(Markup('<b class="em&#34;ph&#34;">"1 &lt; 2"</b>'),
                      escape(tag(tag.b('"1 < 2"', class_='em"ph"'))))
     self.assertEqual(
         Markup('<b class="em&#34;ph&#34;">"1 &lt; 2"</b>'),
         escape(tag(tag.b('"1 < 2"', class_='em"ph"')), quotes=False))
Beispiel #59
0
    def expand_macro(self, formatter, name, content):
        # args will be null if the macro is called without parenthesis.
        if not content:
            return ''
        # 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]]
        # 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' % int(val)
                    elif key in ('margin', 'margin-left', 'margin-right') \
                             and 'display' not in style:
                        style[key] = ' %dpx' % int(val)
                    elif key == 'border':
                        style['border'] = ' %dpx solid' % int(val)
                    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]]
        url = raw_url = desc = None
        attachment = None
        if parts and parts[0] in ('http', 'https', 'ftp', 'data'):  # absolute
            raw_url = url = filespec
            desc = url.rsplit('?')[0]
        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:
            raise TracError(_("No filespec given"))
        if attachment and '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')
            try:
                desc = get_resource_summary(self.env, attachment)
            except ResourceNotFound:
                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))
        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())
        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