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)
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"><%(nick)s>' + '</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
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"><%(nick)s>' + \ '</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
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)
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)
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))
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
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
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
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()
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()
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
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
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)
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)
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")
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)
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))
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")
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)
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)
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)
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)
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))
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 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))
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)
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())
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
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
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) )
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 * ' ' + mod * ' ' 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
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
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))
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
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 * ' ' + mod * ' ') 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
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
def test_escape(self): markup = escape('<b>"&"</b>') assert isinstance(markup, Markup) self.assertEquals('<b>"&"</b>', markup)
def wrap_key(key): return '<p style="word-wrap: break-word; margin: 1em 0">{0}</p>'.format( escape(key))
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
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 * ' ' + mod * ' ' 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
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)
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 * ' ' + mod * ' ' 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
(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
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
def _inlinecode2_formatter(self, match, fullmatch): return '<text:span text:style-name="%s">%s</text:span>' % ( self.get_style('inline'), escape(fullmatch.group('inline2')))
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
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)
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')
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 * ' ' + mod * ' ' 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
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 * ' ' + mod * ' ' 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
def test_escape_noquotes(self): markup = escape('<b>"&"</b>', quotes=False) assert isinstance(markup, Markup) self.assertEquals('<b>"&"</b>', markup)
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))
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 * ' ' + mod * ' ') 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
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))
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 * ' ' + mod * ' ' 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
def test_escape_fragment(self): self.assertEqual(Markup('<b class="em"ph"">"1 < 2"</b>'), escape(tag(tag.b('"1 < 2"', class_='em"ph"')))) self.assertEqual( Markup('<b class="em"ph"">"1 < 2"</b>'), escape(tag(tag.b('"1 < 2"', class_='em"ph"')), quotes=False))
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