def add_message(self, cursor, forum, topic, replyto, author, body): sql = "INSERT INTO message (forum, topic, replyto, time, author," \ " body) VALUES (%s, %s, %s, %s, %s, %s)" self.log.debug(sql % (forum, topic, replyto, int(time.time()), author, body)) cursor.execute(sql, (forum, topic, replyto, int(time.time()), escape(author), escape(body)))
def _get_font(self): # Load the narcissus trac.ini font configuration into an instance variable buf = StringIO() trouble = False if 'narcissus' not in self.config.sections(): msg = 'The narcissus section was not found in the trac configuration file.' buf.write(escape(msg)) self.log.error(msg) trouble = True else: # check for the ttf_path entry self._ttf_path = self.config.get('narcissus', 'ttf_path') if not self._ttf_path: self._ttf = None # PIL will use default system font return None, None if not self._ttf_path[-4:].lower() == '.ttf': msg = 'The ttf_path is set to %s which is not a truetype font file.'\ % self._ttf_path buf.write(escape(msg)) self.log.error(msg) trouble = True if not os.path.exists(self._ttf_path): msg = 'The ttf_path is set to %s but that path does not exist.'\ % self._ttf_path buf.write(escape(msg)) self.log.error(msg) trouble = True self._ttf = ImageFont.truetype(self._ttf_path, 12) return trouble, buf
def render(self, env, req): out = StringIO() can_vote = req.perm.has_permission('POLL_VOTE') if can_vote: out.write('<form id="%(id)s" method="get" action="%(href)s#%(id)s">\n' '<input type="hidden" name="poll" value="%(id)s"/>\n' % {'id': self.key, 'href': env.href(req.path_info)}) out.write('<fieldset class="poll">\n' ' <legend>%s</legend>\n' ' <ul>\n' % escape(self.title)) username = req.authname or 'anonymous' for id, style, vote in self.vote_defs: hid = escape(str(id)) out.write('<li%s>\n' % (style and ' class="%s"' % style or '')) if can_vote: checked = username in self.votes[id] out.write('<input type="radio" name="vote" id="%(pvid)s" value="%(vid)s"%(checked)s/>\n' '<label for="%(pvid)s">%(vote)s</label>\n' % {'vid': hid, 'pvid': self.key + hid, 'vote': vote, 'checked': checked and ' checked="checked"' or ''}) else: out.write(vote) if self.votes[id]: out.write(' <span class="voters">(<span class="voter">' + '</span>, <span class="voter">'.join(self.votes[id]) + '</span>)</span>') out.write('</li>\n') can_vote and out.write('<input type="submit" value="Vote"/>') out.write(' </ul>\n</fieldset>\n') can_vote and out.write('</form>\n') return out.getvalue()
def get_navigation_items(self, req): if req.authname and req.authname != 'anonymous': yield 'metanav', 'login', 'logged in as %s' % req.authname yield 'metanav', 'logout', Markup( '<a href="%s">Logout</a>' \ % escape(self.env.href.logout()) ) else: yield 'metanav', 'login', Markup( '<a href="%s">Login</a>' \ % escape(self.env.href.login()) )
def _load_config(self): # Load the narcissus trac.ini configuration into object instance variables. # The code in this function is modified from the graphviz plugin, (c) Peter Kropf buf = StringIO() trouble = False self.exe_suffix = "" if sys.platform == "win32": self.exe_suffix = ".exe" if "narcissus" not in self.config.sections(): msg = "The narcissus section was not found in the trac configuration file." buf.write(escape(msg)) self.log.error(msg) trouble = True else: # check for the cache_dir entry self.cache_dir = self.config.get("narcissus", "cache_dir") if not self.cache_dir: msg = "The narcissus section is missing the cache_dir field." buf.write(escape(msg)) self.log.error(msg) trouble = True else: if not os.path.exists(self.cache_dir): msg = "The cache_dir is set to %s but that path does not exist." % self.cache_dir buf.write(escape(msg)) self.log.error(msg) trouble = True # check for the cmd_path entry self.cmd_path = None if sys.platform in _CMD_PATHS: self.cmd_path = _CMD_PATHS[sys.platform] self.cmd_path = self.config.get("narcissus", "cmd_path", self.cmd_path) if not self.cmd_path: msg = ( """The narcissus section is missing the cmd_path field and there is no default for %s.""" % sys.platform ) buf.write(escape(msg)) self.log.error(msg) trouble = True elif not os.path.exists(self.cmd_path): msg = "The cmd_path is set to %s but that path does not exist." % self.cmd_path buf.write(escape(msg)) self.log.error(msg) trouble = True # check if we should run the cache manager self.cache_manager = self._boolean(self.config.get("narcissus", "cache_manager", False)) if self.cache_manager: self.cache_max_size = int(self.config.get("narcissus", "cache_max_size", 10000000)) self.cache_min_size = int(self.config.get("narcissus", "cache_min_size", 5000000)) self.cache_max_count = int(self.config.get("narcissus", "cache_max_count", 2000)) self.cache_min_count = int(self.config.get("narcissus", "cache_min_count", 1500)) return trouble, buf
def add_screenshot(self, cursor, name, description, time, author, tags, large_file, medium_file, small_file): sql = "INSERT INTO screenshot (name, description, time, author, tags," \ " large_file, medium_file, small_file) VALUES (%s, %s, %s, %s, %s," \ " %s, %s, %s)" self.log.debug(sql % (name, description, time, author, tags, large_file, medium_file, small_file)) cursor.execute(sql, (escape(name), escape(description), time, escape(author), tags, large_file, medium_file, small_file))
def edit_forum(self, cursor, forum, name, subject, description, moderators, group): moderators = ' '.join(moderators) if not group: group = '0' sql = "UPDATE forum SET name = %s, subject = %s, description = %s," \ " moderators = %s, forum_group = %s WHERE id = %s" self.log.debug(sql % (name, subject, description, moderators, group, forum)) cursor.execute(sql, (escape(name), escape(subject), escape(description), escape(moderators), group, forum))
def get_navigation_items(self, req): if req.authname and req.authname != 'anonymous': yield 'metanav', 'login', Markup('logged in as <b>%s</b>' \ % req.authname) yield 'metanav', 'password', Markup('<a href="%s">Password</a>' \ % escape(self.env.href.password())) yield 'metanav', 'logout', Markup('<a href="%s">Logout</a>' \ % escape(self.env.href.logout())) else: yield 'metanav', 'login', Markup('<a href="%s">Login</a>' \ % escape(self.env.href.login()))
def _add_tags_and_branches(self, req, repos, rev): # TODO: consider pushing that in BrowserModule.process_request and # extend the API with Repository.get_tags and Repository.get_branches tags = [] for t, rev in repos.get_tags(): tags.append({"name": escape(t), "rev": rev}) branches = [] for b, rev in repos.get_branches(): branches.append({"name": escape(b), "rev": rev}) req.hdf["browser.tags"] = tags req.hdf["browser.branches"] = branches
def add_forum(self, cursor, name, author, subject, description, moderators, group): moderators = ' '.join(moderators) if not group: group = '0' sql = "INSERT INTO forum (name, author, time, moderators, subject," \ " description, forum_group) VALUES (%s, %s, %s, %s, %s, %s, %s)" self.log.debug(sql % (name, author, str(int(time.time())), moderators, subject, description, group)) cursor.execute(sql, (escape(name), escape(author), str(int(time.time())), escape(moderators), escape(subject), escape(description), group))
def _update_acronyms(self): page = WikiPage(self.env, self.acronym_page) self.env.log.debug('Updating acronym database') self.acronyms = {} if not page.exists: return for line in page.text.splitlines(): if line.startswith('||') and line.endswith('||') and line[3] != "'": try: a, d, u, s = ([i.strip() for i in line.strip('||').split('||')] + ['', ''])[0:4] assert self.valid_acronym.match(a), "Invalid acronym %s" % a self.acronyms[a] = (escape(d), escape(u), escape(s)) except Exception, e: self.env.log.warning("Invalid acronym line: %s (%s)", line, e)
def render_table(items, colspec, render_item): try: columns = max(int(colspec), 1) except: columns = 3 buf = StringIO() buf.write('<table class="wiki">' ' <tr>') headers = ['<th>Markup </th><th> Display</th>'] * columns buf.write(' <th> </th>'.join(headers)) buf.write(' </tr>') items = items[:] items.sort() rows = [] while items: rows.append(items[0:columns]) items[0:columns] = [] for r in rows: buf.write('<tr>') buf.write('<td> </td>'.join(['<td>%s</td><td>%s</td>' % \ (escape(s), render_item(s)) for s in r])) buf.write('</tr>') buf.write('</table>') return buf.getvalue()
def render(self, req, mimetype, content, filename=None, url=None): if hasattr(content, 'read'): content = content.read() fd, path = tempfile.mkstemp(suffix='.xlsx', text=True) os.write(fd, content) os.close(fd) book = openpyxl.load_workbook(path) buf = [] for sheetName in book.get_sheet_names(): sheet = book.get_sheet_by_name(sheetName) if len(sheet.get_cell_collection()) == 0: continue # Skip empty sheet buf.append(u'<table class="listing"><caption>%s</caption>\n' % escape(sheetName)) buf.append(u'<tbody>') sheet.get_highest_row() rowIdx = 0 for row in sheet.range(sheet.calculate_dimension()): buf.append(u'<tr class="%s">' % (rowIdx % 2 and 'odd' or 'even')) for cell in row: if cell.value == None: val = u'' else: val = cell.value buf.append(u'<td>%s</td>' % val) buf.append(u'</tr>\n') rowIdx = rowIdx + 1 buf.append(u'</tbody>') buf.append(u'</table>\n') os.unlink(path) return u''.join(buf)
def render_macro(self, req, name, content): content = content and REGEX_CRLF.sub("\n", escape(content).replace('"', '"')) or '' def inputstringcallback(match): if match.group('ur'): return '<span class="se-input-userreplacement">' + match.group('ur') + '</span>' if match.group('sq'): m = REGEXP_UR.sub(r'<span class="se-input-userreplacement">\1</span>', match.group('sq')) return '<span class="se-input-string">' + m + '</span>' if match.group('dq'): m = REGEXP_UR.sub(r'<span class="se-input-userreplacement">\1</span>', match.group('dq')) return '<span class="se-input-string">' + m + '</span>' if match.group('ct'): return '<span class="se-input-continuation">' + match.group('ct') + '</span>' if match.group('op'): return '<span class="se-input-option">' + match.group('op') + '</span>' return match.group(0) def linematcher_callback(match): if match.group('preinput'): s = '' if match.group('ps1'): if match.group('ps1start'): s += '<span class="se-prompt-start">' + match.group('ps1start') + '</span>' if match.group('user'): s += '<span class="se-prompt-user">' + match.group('user') + '</span>' if match.group('userhostsep'): s += '<span class="se-prompt-userhostseparator">' + match.group('userhostsep') + '</span>' if match.group('host'): s += '<span class="se-prompt-host">' + match.group('host') + '</span>' if match.group('userpathspace'): s += match.group('userpathspace') if match.group('path'): s += '<span class="se-prompt-path">' + match.group('path') + '</span>' if match.group('ps1end'): s += '<span class="se-prompt-end">' + match.group('ps1end') + '</span>' s = '<span class="se-prompt">' + s + '</span>'; if match.group('cli'): if match.group('cli') == '# ': s += '<span class="se-root">' + match.group('cli') + '</span>' else: s += '<span class="se-unprivileged">' + match.group('cli') + '</span>' input = REGEXP_TAGS.sub(inputstringcallback, match.group('input')) s += '<span class="se-input">' + input + '</span>' return s; if match.group('note'): return '<span class="se-note">' + match.group('note') + '</span>' if match.group('delayedinput'): inputdelayed = REGEXP_TAGS.sub(inputstringcallback, match.group('delayedinput')[3:]) return '<span class="se-input"><span class="se-input-delayed">' + inputdelayed + '</span></span>' if match.group('snippedoutput'): m = match.group('snippedoutput') sniptext = "<Output Snipped>" if len(m) == 5 else m[6:] return '<span class="se-output"><span class="se-output-snipped">' + sniptext + '</span></span>' if match.group('output'): return '<span class="se-output">' + match.group('output') + '</span>' return match.group(0) return Markup('<div class="code"><pre>' + REGEXP_LINE_MATCHER.sub(linematcher_callback, content) + '</pre></div>');
def show_err(self, msg): """Display msg in an error box, using Trac style.""" buf = StringIO() buf.write('<div id="content" class="error"><div class="message"> \n\ <strong>Graphviz macro processor has detected an error. Please fix the problem before continuing.</strong> \n\ <pre>%s</pre> \n\ </div></div>' % escape(msg)) self.log.error(msg) return buf
def render_timeline_event(self, context, field, event): id_, config, label, rev, platform, status, errors = event[3] if field == 'url': return context.href.build(config, id_) elif field == 'title': return tag('Build of ', tag.em('%s [%s]' % (label, rev)), ' on %s %s' % (platform, _status_label[status])) elif field == 'description': message = '' if context.req.args.get('format') == 'rss': if errors: buf = StringIO() prev_step = None for step, error in errors: if step != prev_step: if prev_step is not None: buf.write('</ul>') buf.write('<p>Step %s failed:</p><ul>' \ % escape(step)) prev_step = step buf.write('<li>%s</li>' % escape(error)) buf.write('</ul>') message = Markup(buf.getvalue()) else: if errors: steps = [] for step, error in errors: if step not in steps: steps.append(step) steps = [Markup('<em>%s</em>') % step for step in steps] if len(steps) < 2: message = steps[0] elif len(steps) == 2: message = Markup(' and ').join(steps) elif len(steps) > 2: message = Markup(', ').join(steps[:-1]) + ', and ' + \ steps[-1] message = Markup('Step%s %s failed') % ( len(steps) != 1 and 's' or '', message) return message
def _format_link(self, formatter, ns, target, label): cursor = formatter.db.cursor() cursor.execute("SELECT subject,id FROM mailarc WHERE messageid = %s" , (target,)) row = cursor.fetchone() if row: subject = util.escape(util.shorten_line(row[0])) return '<a href="%s" title="%s">%s</a>' \ % (formatter.href.mailarchive(row[1]), subject, label) else: return label
def _render_directory(self, req, repos, node, rev=None): req.perm.assert_permission('BROWSER_VIEW') order = req.args.get('order', 'name').lower() req.hdf['browser.order'] = order desc = req.args.has_key('desc') req.hdf['browser.desc'] = desc and 1 or 0 info = [] for entry in node.get_entries(): entry_rev = rev and entry.rev info.append({ 'name': entry.name, 'fullpath': entry.path, 'is_dir': int(entry.isdir), 'content_length': entry.content_length, 'size': util.pretty_size(entry.content_length), 'rev': entry.rev, 'permission': 1, # FIXME 'log_href': util.escape(self.env.href.log(entry.path, rev=rev)), 'browser_href': util.escape(self.env.href.peerReviewBrowser(entry.path, rev=rev)) }) changes = get_changes(self.env, repos, [i['rev'] for i in info]) def cmp_func(a, b): dir_cmp = (a['is_dir'] and -1 or 0) + (b['is_dir'] and 1 or 0) if dir_cmp: return dir_cmp neg = desc and -1 or 1 if order == 'date': return neg * cmp(changes[b['rev']]['date_seconds'], changes[a['rev']]['date_seconds']) elif order == 'size': return neg * cmp(a['content_length'], b['content_length']) else: return neg * _natural_order(a['name'].lower(), b['name'].lower()) info.sort(cmp_func) req.hdf['browser.items'] = info req.hdf['browser.changes'] = changes
def _format_link(self, formatter, ns, rev, label): cursor = formatter.db.cursor() cursor.execute('SELECT message FROM revision WHERE rev=%s', (rev,)) row = cursor.fetchone() if row: return '<a class="changeset" title="%s" href="%s">%s</a>' \ % (util.escape(util.shorten_line(row[0])), formatter.href.changeset(rev), label) else: return '<a class="missing changeset" href="%s" rel="nofollow">%s</a>' \ % (formatter.href.changeset(rev), label)
def expand_macro(self, formatter, name, txt): sniffer = csv.Sniffer() txt = txt.encode('ascii', 'replace') reader = csv.reader(StringIO(txt), sniffer.sniff(txt)) formatter.out.write('<table class="wiki">\n') for row in reader: formatter.out.write('<tr>') for col in row: formatter.out.write('<td>%s</td>' % escape(col)) formatter.out.write('</tr>\n') formatter.out.write('</table>\n')
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(_("Canviz macro processor has detected an error. " "Please fix the problem before continuing.")), msg, class_="system-message")
def process_request(self, req): path = req.args.get('path', '/') rev = req.args.get('rev') repos = self.env.get_repository(req.authname) node = get_existing_node(self.env, repos, path, rev) hidden_properties = [ p.strip() for p in self.config.get('browser', 'hide_properties', 'svk:merge').split(',') ] req.hdf['title'] = path req.hdf['browser'] = { 'path': path, 'revision': rev or repos.youngest_rev, 'props': dict([(util.escape(name), util.escape(value)) for name, value in node.get_properties().items() if not name in hidden_properties]), 'href': util.escape( self.env.href.browser(path, rev=rev or repos.youngest_rev)), 'log_href': util.escape(self.env.href.log(path, rev=rev or None)) } path_links = get_path_links(self.env.href, path, rev) if len(path_links) > 1: add_link(req, 'up', path_links[-2]['href'], 'Parent directory') req.hdf['browser.path'] = path_links if node.isdir: req.hdf['browser.is_dir'] = True self._render_directory(req, repos, node, rev) else: self._render_file(req, repos, node, rev) add_stylesheet(req, 'common/css/browser.css') return 'browser.cs', None
def execute(hdf, txt, env): # Currently hdf is set only when the macro is called # From a wiki page if hdf: hdf['wiki.macro.greeting'] = 'Hello World' # args will be `None` if the macro is called without parenthesis. args = txt or 'No arguments' # then, as `txt` comes from the user, it's important to guard against # the possibility to inject malicious HTML/Javascript, by using `escape()`: return 'Hello World, args = ' + escape(args)
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( _("Metapost macro processor has detected an error. " "Please fix the problem before continuing.")), msg, class_="system-message")
def _render_view(self, req, db, page): req.perm.assert_permission('WIKI_VIEW') if page.name == 'WikiStart': req.hdf['title'] = '' else: req.hdf['title'] = escape(page.name) version = req.args.get('version') if version: # Ask web spiders to not index old versions req.hdf['html.norobots'] = 1 txt_href = self.env.href.wiki(page.name, version=version, format='txt') add_link(req, 'alternate', txt_href, 'Plain Text', 'text/plain') req.hdf['wiki'] = { 'page_name': page.name, 'exists': page.exists, 'version': page.version, 'readonly': page.readonly } if page.exists: req.hdf['wiki.page_html'] = wiki_to_html(page.text, self.env, req) history_href = self.env.href.wiki(page.name, action='history') req.hdf['wiki.history_href'] = escape(history_href) else: if not req.perm.has_permission('WIKI_CREATE'): raise TracError('Page %s not found' % page.name) req.hdf['wiki.page_html'] = '<p>Describe "%s" here</p>' % page.name # Show attachments attachments = [] for attachment in Attachment.select(self.env, 'wiki', page.name, db): attachments.append(attachment_to_hdf(self.env, db, req, attachment)) req.hdf['wiki.attachments'] = attachments if req.perm.has_permission('WIKI_MODIFY'): attach_href = self.env.href.attachment('wiki', page.name) req.hdf['wiki.attach_href'] = attach_href
def process_request(self, req): path = req.args.get('path', '/') rev = req.args.get('rev') repos = self.env.get_repository(req.authname) try: node = get_existing_node(self.env, repos, path, rev) except: rev = repos.youngest_rev node = get_existing_node(self.env, repos, path, rev) hidden_properties = [p.strip() for p in self.config.get('browser', 'hide_properties', 'svk:merge').split(',')] req.hdf['title'] = path req.hdf['browser'] = { 'path': path, 'revision': rev or repos.youngest_rev, 'props': dict([(util.escape(name), util.escape(value)) for name, value in node.get_properties().items() if not name in hidden_properties]), 'href': util.escape(self.env.href.peerReviewBrowser(path, rev=rev or repos.youngest_rev)), 'log_href': util.escape(self.env.href.log(path, rev=rev or None)) } path_links = self.get_path_links_CRB(self.env.href, path, rev) if len(path_links) > 1: add_link(req, 'up', path_links[-2]['href'], 'Parent directory') req.hdf['browser.path'] = path_links if node.isdir: req.hdf['browser.is_dir'] = True self._render_directory(req, repos, node, rev) else: self._render_file(req, repos, node, rev) add_stylesheet(req, 'common/css/browser.css') return 'peerReviewBrowser.cs', None
def render_macro(self, req, name, content): query_string = '' compact = 0 argv = content.split(',') if len(argv) > 0: query_string = argv[0] if len(argv) > 1: if argv[1].strip().lower() == 'compact': compact = 1 try: from cStringIO import StringIO except NameError: from StringIO import StringIO buf = StringIO() query = Query.from_string(self.env, query_string) query.order = 'id' tickets = query.execute() if tickets: if compact: links = [] for ticket in tickets: href = self.env.href.ticket(int(ticket['id'])) summary = escape(shorten_line(ticket['summary'])) links.append( '<a class="%s ticket" href="%s" ' 'title="%s">#%s</a>' % (ticket['status'], href, summary, ticket['id'])) buf.write(', '.join(links)) else: buf.write('<dl class="wiki compact">') for ticket in tickets: href = self.env.href.ticket(int(ticket['id'])) buf.write('<dt><a href="%s">#%s</a></dt>' % (href, ticket['id'])) buf.write('<dd>%s</dd>' % (escape(ticket['summary']))) buf.write('</dl>') return buf.getvalue()
def get_timeline_events(self, req, start, stop, filters): if 'changeset' in filters: format = req.args.get('format') show_files = int( self.config.get('timeline', 'changeset_show_files')) db = self.env.get_db_cnx() repos = self.env.get_repository() rev = repos.youngest_rev while rev: chgset = repos.get_changeset(rev) if chgset.date < start: return if chgset.date < stop: excerpt = util.shorten_line(chgset.message or '--') if format == 'rss': title = 'Changeset <em>[%s]</em>: %s' % (util.escape( chgset.rev), util.escape(excerpt)) href = self.env.abs_href.changeset(chgset.rev) message = wiki_to_html(chgset.message or '--', self.env, db, absurls=True) else: title = 'Changeset <em>[%s]</em> by %s' % (util.escape( chgset.rev), util.escape(chgset.author)) href = self.env.href.changeset(chgset.rev) message = wiki_to_oneliner(excerpt, self.env, db) if show_files: files = [] for chg in chgset.get_changes(): if show_files > 0 and len(files) >= show_files: files.append('...') break files.append('<span class="%s">%s</span>' % (chg[2], util.escape(chg[0]))) message = '<span class="changes">' + ', '.join(files) +\ '</span>: ' + message yield 'changeset', href, title, chgset.date, chgset.author,\ message rev = repos.previous_rev(rev)
def render_macro(self, req, name, content): #if req.hdf: # if not req.hdf.has_key("macro.ShellExample.outputcss"): # req.hdf["macro.ShellExample.outputcss"] = True content = content and self.regex_crlf.sub("\n", escape(content).replace('"', '"')) or '' def stringcallback(match): if match.group('ur'): return '<span class="code-input-userreplacement">' + match.group('ur') + '</span>' if match.group('sq'): m = self.regexp_ur.sub(r'<span class="code-input-userreplacement">\1</span>', match.group('sq')) return '<span class="code-input-string">' + m + '</span>' if match.group('dq'): m = self.regexp_ur.sub(r'<span class="code-input-userreplacement">\1</span>', match.group('dq')) return '<span class="code-input-string">' + m + '</span>' if match.group('ct'): return '<span class="code-input-continuation">' + match.group('ct') + '</span>' if match.group('op'): return '<span class="code-input-option">' + match.group('op') + '</span>' return match.group(0) def callback(match): m = match.group('cli') if m: path = match.group('path') if path: line = '<span class="code-path">' + path + '</span>' else: line = '' input = self.regexp_tags.sub(stringcallback, match.group('input')) input = '<span class="code-input">' + input + '</span>' if m == '# ': line = line + '<span class="code-root">' + m + input + '</span>' else: line = line + '<span class="code-user">' + m + input + '</span>' return line m = match.group('note') if m: return '<span class="code-note">' + m + '</span>' m = match.group('delayedinput') if m: inputdelayed = self.regexp_tags.sub(stringcallback, m[3:]) return '<span class="code-input"><span class="code-delayed">' + inputdelayed + '</span></span>' m = match.group('snippedoutput') if m: sniptext = "<Output Snipped>" if len(m) == 5 else m[6:] return '<span class="code-output"><span class="code-snipped">' + sniptext + '</span></span>' m = match.group('output') if m: return '<span class="code-output">' + m + '</span>' return match.group(0) return Markup('<div class="code"><pre>' + self.regexp.sub(callback, content) + '</pre></div>');
def get_search_results(self, req, query, filters): if not 'ticket' in filters: return db = self.env.get_db_cnx() sql = "SELECT DISTINCT a.summary,a.description,a.reporter, " \ "a.keywords,a.id,a.time FROM ticket a " \ "LEFT JOIN ticket_change b ON a.id = b.ticket " \ "WHERE (b.field='comment' AND %s ) OR " \ "%s OR %s OR %s OR %s OR %s" % \ (query_to_sql(db, query, 'b.newvalue'), query_to_sql(db, query, 'summary'), query_to_sql(db, query, 'keywords'), query_to_sql(db, query, 'description'), query_to_sql(db, query, 'reporter'), query_to_sql(db, query, 'cc')) cursor = db.cursor() cursor.execute(sql) for summary, desc, author, keywords, tid, date in cursor: yield (self.env.href.ticket(tid), '#%d: %s' % (tid, util.escape(util.shorten_line(summary))), date, author, util.escape(shorten_result(desc, query.split())))
def edit_screenshot(self, cursor, screenshot, name, description, tags, components, versions): # Update screenshot values. sql = "UPDATE screenshot SET name = %s, description = %s, tags = %s" \ " WHERE id = %s" self.log.debug(sql % (name, description, tags, screenshot)) cursor.execute(sql, (escape(name), escape(description), tags, screenshot)) # Replace components sql = "DELETE FROM screenshot_component WHERE screenshot = %s" self.log.debug(sql % (screenshot,)) cursor.execute(sql, (screenshot,)) for component in components: self.add_component(cursor, screenshot, component) # Replace versions sql = "DELETE FROM screenshot_version WHERE screenshot = %s" self.log.debug(sql % (screenshot,)) cursor.execute(sql, (screenshot,)) for version in versions: self.add_version(cursor, screenshot, version)
def get_path_links(href, path, rev): links = [] parts = path.split('/') if not parts[-1]: parts.pop() path = '/' for part in parts: path = path + part + '/' links.append({ 'name': part or 'root', 'href': escape(href.browser(path, rev=rev)) }) return links
def get_timeline_events(self, req, start, stop, filters): if 'wiki' in filters: format = req.args.get('format') db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute( "SELECT time,name,comment,author " "FROM wiki WHERE time>=%s AND time<=%s", (start, stop)) for t, name, comment, author in cursor: title = '<em>%s</em> edited by %s' % (escape(name), escape(author)) if format == 'rss': href = self.env.abs_href.wiki(name) comment = wiki_to_html(comment or '--', self.env, db, absurls=True) else: href = self.env.href.wiki(name) comment = wiki_to_oneliner(shorten_line(comment), self.env, db) yield 'wiki', href, title, t, author, comment
def get_path_links_CRB(self, href, path, rev): links = [] parts = path.split('/') if not parts[-1]: parts.pop() path = '/' for part in parts: path = path + part + '/' links.append({ 'name': part or 'root', 'href': util.escape(href.peerReviewBrowser(path, rev=rev)) }) return links
def _format_link(self, formatter, ns, target, label): cursor = formatter.db.cursor() cursor.execute("SELECT summary,status FROM ticket WHERE id=%s", (target,)) row = cursor.fetchone() if row: summary = util.escape(util.shorten_line(row[0])) return '<a class="%s ticket" href="%s" title="%s (%s)">%s</a>' \ % (row[1], formatter.href.ticket(target), summary, row[1], label) else: return '<a class="missing ticket" href="%s" rel="nofollow">%s</a>' \ % (formatter.href.ticket(target), label)
def _format_link(self, formatter, ns, target, label): cursor = formatter.db.cursor() cursor.execute("SELECT summary,status FROM ticket WHERE id=%s", (target, )) row = cursor.fetchone() if row: summary = util.escape(util.shorten_line(row[0])) return '<a class="%s ticket" href="%s" title="%s (%s)">%s</a>' \ % (row[1], formatter.href.ticket(target), summary, row[1], label) else: return '<a class="missing ticket" href="%s" rel="nofollow">%s</a>' \ % (formatter.href.ticket(target), label)
def edit_screenshot(self, cursor, screenshot, name, description, tags, components, versions): # Update screenshot values. sql = "UPDATE screenshot SET name = %s, description = %s, tags = %s" \ " WHERE id = %s" self.log.debug(sql % (name, description, tags, screenshot)) cursor.execute(sql, (escape(name), escape(description), tags, screenshot)) # Replace components sql = "DELETE FROM screenshot_component WHERE screenshot = %s" self.log.debug(sql % (screenshot, )) cursor.execute(sql, (screenshot, )) for component in components: self.add_component(cursor, screenshot, component) # Replace versions sql = "DELETE FROM screenshot_version WHERE screenshot = %s" self.log.debug(sql % (screenshot, )) cursor.execute(sql, (screenshot, )) for version in versions: self.add_version(cursor, screenshot, version)
def execute(hdf, txt, env): sniffer = csv.Sniffer() txt = txt.encode('ascii', 'replace') reader = csv.reader(StringIO(txt), sniffer.sniff(txt)) out = StringIO() out.write('<table class="wiki">\n') for row in reader: out.write('<tr>') for col in row: out.write('<td>%s</td>' % escape(col)) out.write('</tr>\n') out.write('</table>\n') return out.getvalue()
def _render_history(self, req, db, page): """Extract the complete history for a given page and stores it in the HDF. This information is used to present a changelog/history for a given page. """ req.perm.assert_permission('WIKI_VIEW') if not page.exists: raise TracError, "Page %s does not exist" % page.name req.hdf['title'] = escape(page.name) + ' (history)' history = [] for version, t, author, comment, ipnr in page.get_history(): history.append({ 'url': escape(self.env.href.wiki(page.name, version=version)), 'diff_url': escape( self.env.href.wiki(page.name, version=version, action='diff')), 'version': version, 'time': format_datetime(t), 'time_delta': pretty_timedelta(t), 'author': escape(author), 'comment': wiki_to_oneliner(comment or '', self.env, db), 'ipaddr': ipnr }) req.hdf['wiki.history'] = history
def _load_config(self): # Load the narcissus trac.ini configuration into object instance variables. # The code in this function is modified from the graphviz plugin, (c) Peter Kropf buf = StringIO() trouble = False self.exe_suffix = '' if sys.platform == 'win32': self.exe_suffix = '.exe' # check for the cache_dir entry if not self.cache_dir: msg = 'The narcissus section is missing the cache_dir field.' buf.write(escape(msg)) self.log.error(msg) trouble = True if self.cache_dir and not os.path.exists(self.cache_dir): msg = 'The cache_dir is set to %s but that path does not exist.'\ % self.cache_dir buf.write(escape(msg)) self.log.error(msg) trouble = True # check for the cmd_path entry if not self.cmd_path: msg = '''The narcissus section is missing the cmd_path field and there is no default for %s.''' % sys.platform buf.write(escape(msg)) self.log.error(msg) trouble = True if self.cmd_path and not os.path.exists(self.cmd_path): msg = 'The cmd_path is set to %s but that path does not exist.' % self.cmd_path buf.write(escape(msg)) self.log.error(msg) trouble = True return trouble, buf
def expand_macro(self, formatter, name, args): text = '' if args: lines = args.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 '<pre class="wiki">%s</pre>' % escape(text)
def get_timeline_events(self, req, start, stop, filters): if 'changeset' in filters: format = req.args.get('format') show_files = int( self.config.get('timeline', 'changeset_show_files')) db = self.env.get_db_cnx() repos = self.env.get_repository() authzperm = SubversionAuthorizer(self.env, req.authname) rev = repos.youngest_rev while rev: if not authzperm.has_permission_for_changeset(rev): rev = repos.previous_rev(rev) continue chgset = repos.get_changeset(rev) if chgset.date < start: return if chgset.date < stop: message = chgset.message or '--' if format == 'rss': title = util.Markup('Changeset <em>[%s]</em>: %s', chgset.rev, util.shorten_line(message)) href = self.env.abs_href.changeset(chgset.rev) message = wiki_to_html(message, self.env, req, db, absurls=True) else: title = util.Markup('Changeset <em>[%s]</em> by %s', chgset.rev, chgset.author) href = self.env.href.changeset(chgset.rev) message = wiki_to_oneliner(message, self.env, db, shorten=True) if show_files: files = [] for chg in chgset.get_changes(): if show_files > 0 and len(files) >= show_files: files.append('...') break files.append('<span class="%s">%s</span>' % (chg[2], util.escape(chg[0]))) message = '<span class="changes">' + ', '.join(files) +\ '</span>: ' + message yield 'changeset', href, title, chgset.date, chgset.author,\ util.Markup(message) rev = repos.previous_rev(rev)
def _format_link(self, formatter, ns, path, label): match = IMG_RE.search(path) if formatter.flavor != 'oneliner' and match: return '<img src="%s" alt="%s" />' % \ (formatter.href.file(path, format='raw'), label) path, rev, line = get_path_rev_line(path) if line is not None: anchor = '#L%d' % line else: anchor = '' label = urllib.unquote(label) return '<a class="source" href="%s%s">%s</a>' \ % (util.escape(formatter.href.peerReviewBrowser(path, rev=rev)), anchor, label)
def attachment_to_hdf(env, db, req, attachment): from trac.wiki import wiki_to_oneliner if not db: db = env.get_db_cnx() hdf = { 'filename': attachment.filename, 'description': wiki_to_oneliner(attachment.description, env, db), 'author': util.escape(attachment.author), 'ipnr': attachment.ipnr, 'size': util.pretty_size(attachment.size), 'time': time.strftime('%c', time.localtime(attachment.time)), 'href': attachment.href() } return hdf
def render(self, env, req): out = StringIO() can_vote = req.perm.has_permission('POLL_VOTE') if can_vote: out.write('<form id="%(id)s" method="get" action="%(href)s#%(id)s">\n' '<input type="hidden" name="poll" value="%(id)s"/>\n' % {'id': self.key, 'href': env.href(req.path_info)}) out.write('<fieldset class="poll">\n' ' <legend>%s</legend>\n' ' <ul>\n' % escape(self.title)) username = req.authname or 'anonymous' for id, style, vote in self.vote_defs: hid = escape(str(id)) out.write('<li%s>\n' % (style and ' class="%s"' % style or '')) if can_vote: checked = username in self.votes[id] out.write('<input type="radio" name="vote" id="%(pvid)s" value="%(vid)s"%(checked)s/>\n' '<label for="%(pvid)s">%(vote)s</label>\n' % {'vid': hid, 'pvid': self.key + hid, 'vote': vote, 'checked': checked and ' checked="checked"' or ''}) else: out.write(vote) if self.votes[id]: out.write(' <span class="voters">(<span class="voter">' + '</span>, <span class="voter">'.join(self.votes[id]) + '</span>)</span>') out.write('</li>\n') if can_vote: out.write('<input type="submit" value="Vote"/>') else: out.write("<br/><i>You don't have permission to vote. You may need to login.</i>") out.write(' </ul>\n</fieldset>\n') can_vote and out.write('</form>\n') return out.getvalue()
def display_rss(self, req, query): query.verbose = True db = self.env.get_db_cnx() results = query.execute(db) for result in results: result['href'] = self.env.abs_href.ticket(result['id']) if result['reporter'].find('@') == -1: result['reporter'] = '' if result['description']: result['description'] = escape(wiki_to_html(result['description'] or '', self.env, req, db, absurls=1)) if result['time']: result['time'] = http_date(result['time']) req.hdf['query.results'] = results
def _get_font(self): # Load the narcissus trac.ini font configuration into an instance variable buf = StringIO() trouble = False # check for the ttf_path entry if not self._ttf_path: self._ttf = None # PIL will use default system font return None, None if not self._ttf_path[-4:].lower() == '.ttf': msg = 'The ttf_path is set to %s which is not a truetype font file.'\ % self._ttf_path buf.write(escape(msg)) self.log.error(msg) trouble = True if not os.path.exists(self._ttf_path): msg = 'The ttf_path is set to %s but that path does not exist.'\ % self._ttf_path buf.write(escape(msg)) self.log.error(msg) trouble = True self._ttf = ImageFont.truetype(self._ttf_path, 12) return trouble, buf
def render_macro(self, req, name, content): prefix = limit = None if content: argv = [arg.strip() for arg in content.split(',')] if len(argv) > 0: prefix = argv[0].replace('\'', '\'\'') if len(argv) > 1: limit = int(argv[1]) db = self.env.get_db_cnx() cursor = db.cursor() sql = 'SELECT name, max(time) FROM wiki ' if prefix: sql += "WHERE name LIKE '%s%%' " % prefix sql += 'GROUP BY name ORDER BY max(time) DESC' if limit: sql += ' LIMIT %d' % limit cursor.execute(sql) buf = StringIO() prevdate = None for name, time in cursor: date = format_date(time) if date != prevdate: if prevdate: buf.write('</ul>') buf.write('<h3>%s</h3><ul>' % date) prevdate = date buf.write('<li><a href="%s">%s</a></li>\n' % (escape(self.env.href.wiki(name)), escape(name))) if prevdate: buf.write('</ul>') return buf.getvalue()
def _render_confirm_delete(self, req, db, id): req.perm.assert_permission('REPORT_DELETE') cursor = db.cursor() cursor.execute("SELECT title FROM report WHERE id = %s", (id,)) row = cursor.fetchone() if not row: raise util.TracError('Report %s does not exist.' % id, 'Invalid Report Number') req.hdf['title'] = 'Delete Report {%s} %s' % (id, row[0]) req.hdf['report'] = { 'id': id, 'mode': 'delete', 'title': util.escape(row[0]), 'href': self.env.href.report(id) }
def render_macro(self, req, name, content): from trac.wiki.formatter import wiki_to_html from trac.wiki import WikiSystem buf = StringIO() buf.write("<dl>") wiki = WikiSystem(self.env) for macro_provider in wiki.macro_providers: for macro_name in macro_provider.get_macros(): buf.write("<dt><code>[[%s]]</code></dt>" % escape(macro_name)) description = macro_provider.get_macro_description(macro_name) if description: buf.write("<dd>%s</dd>" % wiki_to_html(description, self.env, req)) buf.write("</dl>") return buf.getvalue()
def _build_graph(self, req, tkt_ids, label_summary=0): g = graphviz.Graph() g.label_summary = label_summary g.attributes['rankdir'] = self.graph_direction node_default = g['node'] node_default['style'] = 'filled' edge_default = g['edge'] edge_default['style'] = '' # Force this to the top of the graph for id in tkt_ids: g[id] if self.show_key: g[-1]['label'] = self.closed_text g[-1]['fillcolor'] = self.closed_color g[-1]['shape'] = 'box' g[-2]['label'] = self.opened_text g[-2]['fillcolor'] = self.opened_color g[-2]['shape'] = 'box' links = TicketLinks.walk_tickets(self.env, tkt_ids, full=self.full_graph) links = sorted(links, key=lambda link: link.tkt.id) for link in links: tkt = link.tkt node = g[tkt.id] if label_summary: node['label'] = u'#%s %s' % (tkt.id, tkt['summary']) else: node['label'] = u'#%s' % tkt.id node['fillcolor'] = tkt[ 'status'] == 'closed' and self.closed_color or self.opened_color node['URL'] = req.href.ticket(tkt.id) node['alt'] = u'Ticket #%s' % tkt.id node['tooltip'] = escape(tkt['summary']) if self.highlight_target and tkt.id in tkt_ids: node['penwidth'] = 3 for n in link.blocking: node > g[n] return g
def _update_replace(self): self.env.log.debug('Updating replace database') page = WikiPage(self.env, self.replace_page) self.replace = {} if not page.exists: return for line in page.text.splitlines(): self.env.log.warning(line) line = line.rstrip() if line.startswith('||') and line.endswith( '||') and line[3] != "'": try: a, d = ([i.strip() for i in line.strip('||').split('||')] + ['', ''])[0:2] assert self.valid_replace.match( a), "Invalid replaces %s" % a self.replace[a] = (escape(d)) except Exception, d: self.env.log.warning("Invalid replaces line: %s", line)
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 handle_match(self, fullmatch): for itype, match in fullmatch.groupdict().items(): # handle ticket-links if match and match.startswith("#") and itype == "i3": if match[1:].isdigit(): return r"\ticket{%s}" % match[1:] if match and not itype in self.wikiparser.helper_patterns: # Check for preceding escape character '!' if match[0] == '!': return self.escape_verb(escape(match[1:])) if itype in self.wikiparser.external_handlers: if itype in ["i0", "i1", "i2"]: return match external_handler = self.wikiparser.external_handlers["i0"] ret = external_handler(self, match, fullmatch) return ret else: internal_handler = getattr(self, '_%s_formatter' % itype) ret = internal_handler(match, fullmatch) return ret
def process_request(self, req): req.perm.assert_permission('ROADMAP_VIEW') req.hdf['title'] = 'Roadmap' showall = req.args.get('show') == 'all' req.hdf['roadmap.showall'] = showall db = self.env.get_db_cnx() milestones = [] for idx, milestone in enum(Milestone.select(self.env, showall)): hdf = milestone_to_hdf(self.env, db, req, milestone) milestones.append(hdf) req.hdf['roadmap.milestones'] = milestones for idx,milestone in enum(milestones): prefix = 'roadmap.milestones.%d.' % idx tickets = get_tickets_for_milestone(self.env, db, milestone['name'], 'owner') req.hdf[prefix + 'stats'] = calc_ticket_stats(tickets) for k, v in get_query_links(self.env, milestone['name']).items(): req.hdf[prefix + 'queries.' + k] = escape(v) milestone['tickets'] = tickets # for the iCalendar view if req.args.get('format') == 'ics': self.render_ics(req, db, milestones) return add_stylesheet(req, 'common/css/roadmap.css') # FIXME should use the 'webcal:' scheme, probably username = None if req.authname and req.authname != 'anonymous': username = req.authname icshref = self.env.href.roadmap(show=req.args.get('show'), user=username, format='ics') add_link(req, 'alternate', icshref, 'iCalendar', 'text/calendar', 'ics') return 'roadmap.cs', None