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 _parse_heading(self, match, fullmatch, shorten): match = match.strip() depth = min(len(fullmatch.group('hdepth')), 5) anchor = fullmatch.group('hanchor') or '' heading_text = match[depth + 1:-depth - 1 - len(anchor)] out = StringIO() TiddlyWikiFormatter(self.env, self.req).format(heading_text, out) heading = Markup(out.getvalue().strip()) if anchor: anchor = anchor[1:] else: sans_markup = heading.plaintext(keeplinebreaks=False) anchor = self._anchor_re.sub('', sans_markup) if not anchor or anchor[0].isdigit() or anchor[0] in '.-': # an ID must start with a Name-start character in XHTML anchor = 'a' + anchor # keeping 'a' for backward compat i = 1 anchor_base = anchor while anchor in self._anchors: anchor = anchor_base + str(i) i += 1 self._anchors[anchor] = True if shorten: heading = wiki_to_oneliner(heading_text, self.env, self.db, True, self._absurls) return (depth, heading, anchor)
def generate_help(self, target, inline=False, visibility=''): """Show documentation for named `target`. If `inline` is set, no header will be generated. For the `visibility` argument, see `PyDocMacro`. """ try: if not target or target == 'index': if inline: doc = '' else: doc = html.h1('Python: Index of Modules') for dir in self.syspath: if os.path.isdir(dir): doc += Markup( self.doc.index(dir, includes=self.includes, excludes=self.excludes)) return doc else: if inline: doc = '' else: doc = html.h1('Python: Documentation for ', Markup(self.doc._dotted_path_links(target))) return doc + Markup( to_unicode(self._makedoc(target, visibility))) except ImportError: return "No Python documentation found for '%s'" % target
def trac_get_reference(env, context, rawtext, target, text): fulltext = target + ' ' + text if text else target link = extract_link(env, context, fulltext) uri = None missing = False if isinstance(link, (Element, Fragment)): linktext = Markup(link).striptags() # the following is a bit hackish, but it takes into account: # - an eventual trailing '?' for missing wiki pages # - space eventually introduced due to split_page_names option if linktext.rstrip('?').replace(' ', '') != target: text = linktext elt = find_element(link, 'href', 'missing') if elt is not None: uri = elt.attrib.get('href', '') missing = 'missing' in elt.attrib.get('class', '').split() else: uri = context.href.wiki(target) missing = not WikiSystem(env).has_page(target) if uri or missing: reference = nodes.reference(rawtext, text or target) reference['refuri'] = uri if missing: reference['classes'].append('missing') return reference
def wiki_to_html(self, wikitext, req): self.env.log.debug('start function wiki_to_html') # pylint: disable-msg=E1101 # Remove some macros (TOC is better handled in ODT itself) for macro in self.remove_macros: wikitext = re.sub('\[\[%s(\([^)]*\))?\]\]' % macro, "", wikitext) # Now convert wiki to HTML out = StringIO() context = Context.from_request(req, absurls=True) Formatter(self.env, # pylint: disable-msg=E1101 context('wiki', self.page_name)).format(wikitext, out) html = Markup(out.getvalue()) html = html.encode("utf-8", 'replace') # Clean up the HTML html = re.sub('<span class="icon">.</span>', '', html) # Remove external link icon tidy_options = dict(output_xhtml=1, add_xml_decl=1, indent=1, tidy_mark=0, input_encoding='utf8', output_encoding='utf8', doctype='auto', wrap=0, char_encoding='utf8') html = tidy.parseString(html, **tidy_options) # Replace nbsp with entity: # http://www.mail-archive.com/[email protected]/msg03670.html html = str(html).replace(" ", " ") # Tidy creates newlines after <pre> (by indenting) html = re.sub('<pre([^>]*)>\n', '<pre\\1>', html) return html
def _parse_heading(self, match, fullmatch, shorten): match = match.strip() depth = min(len(fullmatch.group('hdepth')), 5) anchor = fullmatch.group('hanchor') or '' heading_text = match[depth+1:-depth-1-len(anchor)] out = StringIO() TiddlyWikiFormatter(self.env, self.req).format(heading_text, out) heading = Markup(out.getvalue().strip()) if anchor: anchor = anchor[1:] else: sans_markup = heading.plaintext(keeplinebreaks=False) anchor = self._anchor_re.sub('', sans_markup) if not anchor or anchor[0].isdigit() or anchor[0] in '.-': # an ID must start with a Name-start character in XHTML anchor = 'a' + anchor # keeping 'a' for backward compat i = 1 anchor_base = anchor while anchor in self._anchors: anchor = anchor_base + str(i) i += 1 self._anchors[anchor] = True if shorten: heading = wiki_to_oneliner(heading_text, self.env, self.db, True, self._absurls) return (depth, heading, anchor)
def test_check(self): check = BasicCheck(self.env) req = self.req # Inspector doesn't provide additional fields. field_res = check.render_registration_fields(req, {}) self.assertEqual(len(field_res), 2) self.assertEqual((Markup(field_res[0]), field_res[1]), (Markup(''), {})) # 1st attempt: No input. self.assertRaises(RegistrationError, check.validate_registration, req) # 2nd attempt: Illegal character. req.args['username'] = '******' self.assertRaises(RegistrationError, check.validate_registration, req) # 3rd attempt: All upper-case word. req.args['username'] = '******' self.assertRaises(RegistrationError, check.validate_registration, req) # 4th attempt: Reserved word. req.args['username'] = '******' self.assertRaises(RegistrationError, check.validate_registration, req) # 5th attempt: Existing user. req.args['username'] = '******' self.assertRaises(RegistrationError, check.validate_registration, req) # 6th attempt: Valid username, but no password. req.args['username'] = '******' self.assertRaises(RegistrationError, check.validate_registration, req) # 7th attempt: Valid username, no matching passwords. req.args['password'] = '******' self.assertRaises(RegistrationError, check.validate_registration, req) # 8th attempt: Finally some valid input. req.args['password_confirm'] = 'password' self.assertEqual(check.validate_registration(req), None)
def test_tag(self): self.assertEqual(Markup('0<a>0</a> and <b>0</b> and <c></c>' ' and <d class="a b" more_="[\'a\']"></d>'), Markup(tag(0, tag.a(0, href=''), b' and ', tag.b(0.0), ' and ', tag.c(None), ' and ', tag.d('', class_=['a', '', 'b'], more__=[b'a']))))
def test_check(self): check = BotTrapCheck(self.env) req = self.req # Inspector provides the email text input field. wrong_input = "Hey, I'm a bot." data = dict(basic_token=wrong_input) req.args.update(data) field_res = check.render_registration_fields(req, data) self.assertEqual(len(field_res), 2) self.assertTrue(Markup(field_res[0]).startswith('<label>Parole:')) # 1st attempt: Wrong input. self.assertRaises(RegistrationError, check.validate_registration, req) # Ensure, that old input is restored on failure. self.assertTrue(wrong_input in Markup(field_res[0])) # Ensure, that template data dict is passed unchanged. self.assertEqual(field_res[1], data) # 2nd attempt: No input. req.args['basic_token'] = '' self.assertRaises(RegistrationError, check.validate_registration, req) # 3rd attempt: As before, but request done by authenticated user. req = Mock(authname='admin', args=self.req.args) self.assertEqual(check.validate_registration(req), None) # 4th attempt: Finally valid input. req = self.req req.args['basic_token'] = self.basic_token self.assertEqual(check.validate_registration(req), None) # 5th attempt: Fill the hidden field too. req.args['sentinel'] = "Humans can't see this? Crap - I'm superior!" self.assertRaises(RegistrationError, check.validate_registration, req)
def get_timeline_events(self, req, start, stop, filters): format = req.args.get('format') status_map = { 'new': ('newticket', u'créé'), 'reopened': ('newticket', u'réouvert'), 'closed': ('closedticket', u'fermé'), 'edit': ('editedticket', u'mis à jour') } href = format == 'rss' and req.abs_href or req.href def produce( (id, t, author, type, summary), status, fields, comment, cid): if status == 'edit': if 'ticket_details' in filters: info = u'' if len(fields) > 0: info = u', '.join([u'<i>%s</i>' % f for f in \ fields.keys()]) + u' modifié<br />' else: return None elif 'ticket' in filters: if status == 'closed' and fields.has_key('resolution'): info = fields['resolution'] if info and comment: info = '%s: ' % info else: info = '' else: return None kind, verb = status_map[status] if format == 'rss': title = u'Ticket #%s (%s %s): %s' % \ (id, translate(self.env, type).lower(), verb, summary) else: title = Markup( u'Ticket <em title="%s">#%s</em> (%s) %s par %s', summary, id, translate(self.env, type), verb, author) ticket_href = href.ticket(id) if cid: ticket_href += '#comment:' + cid if status == 'new': message = unicode(summary) else: message = Markup(info) if comment: if format == 'rss': message += wiki_to_html(comment, self.env, req, db, absurls=True) else: message += wiki_to_oneliner(comment, self.env, db, shorten=True) return kind, ticket_href, title, t, author, message
def post_process_request(self, req, template, data, content_type): if data and req.path_info == '/timeline' and \ 'TAGS_VIEW' in req.perm(Resource('tags')): def realm_handler(_, node, context): return query.match(node, [context.realm]) query_str = req.args.getfirst(self.key) if query_str is None and req.args.get('format') != 'rss': query_str = req.session.get('timeline.%s' % self.key) else: query_str = (query_str or '').strip() # Record tag query expression between visits. req.session['timeline.%s' % self.key] = query_str if data.get('events') and query_str: tag_system = TagSystem(self.env) try: query = Query(query_str, attribute_handlers={'realm': realm_handler}) except InvalidQuery as e: add_warning(req, _("Tag query syntax error: %s" % e)) else: all_realms = tag_system.get_taggable_realms(req.perm) query_realms = set() for m in REALM_RE.finditer(query.as_string()): query_realms.add(m.group(1)) # Don't care about resources from non-taggable realms. realms = not query_realms and all_realms or \ query_realms.intersection(all_realms) events = [] self.log.info("Filtering timeline events by tags '%s'", query_str) for event in data['events']: resource = resource_from_event(event) if resource and resource.realm in realms: # Shortcut view permission checks here. tags = tag_system.get_tags(None, resource) if query(tags, context=resource): events.append(event) # Overwrite with filtered list. data['events'] = events if query_str: # Add current value for next form rendering. data[self.key] = query_str elif self.key in req.session: del req.session[self.key] filter_lst = [] # xpath = '//form[@id="prefs"]/div[1]' xform = JTransformer('form#prefs > div:nth-of-type(1)') insert = builder(Markup('<br />'), tag_("matching tags "), builder.input(type='text', name=self.key, value=data.get(self.key))) filter_lst.append(xform.append(Markup(insert))) add_script_data(req, {'tags_filter': filter_lst}) add_script(req, 'tags/js/tags_jtransform.js') return template, data, content_type
def post_process_request(self, req, template, content_type): if req.path_info.startswith('/ticket'): tkt_id = req.path_info[8:] # jQuery! add_script(req, 'mastertickets/jquery.js') # Add in the 'Blocked by' field blocking_ids = blocked_by(self.env, tkt_id) if blocking_ids: req.hdf['ticket.blockedby'] = ', '.join( [str(x) for x in blocking_ids]) req.hdf['ticket.fields.blockedby'] = { 'value': '', 'custom': 1, 'type': 'text', 'label': 'Blocked By', 'order': 10, # Force this to be at the end, since I am going to disappear it. } add_stylesheet(req, 'mastertickets/ticket.css') add_script(req, 'mastertickets/linkify_blockedby.js') # If any blockers are not closed, disable the resovle option img_src, img_alt = 'checkmark.gif', 'Blockers closed' for i in blocking_ids: if Ticket(self.env, i)['status'] != 'closed': if Ticket(self.env, tkt_id)['status'] != 'closed': add_script(req, 'mastertickets/disable_resolve.js') img_src, img_alt = 'x.png', 'Blockers open' else: img_src, img_alt = 'caution.png', 'Blockers open, but current ticket closed' # Magic stuff in the footer req.hdf['project.footer'] = Markup( req.hdf['project.footer'] + Markup( html.DIV(html.IMG(class_='blockedby_icon', src=req.href.chrome( 'mastertickets', img_src), alt=img_alt, title=img_alt), ' ', linkify_ids(self.env, req, blocking_ids), id='linkified_blockedby', style='display:none'))) # Linkify the 'Blocks' field blocks_ids = req.hdf.get('ticket.blocking') or '' blocks_ids = blocks_ids.replace('#', '') if blocks_ids: blocks_ids = [x.strip() for x in blocks_ids.split(',')] req.hdf['project.footer'] = Markup( req.hdf['project.footer'] + Markup( html.DIV(linkify_ids(self.env, req, blocks_ids), id='linkified_blocking', style='display:none'))) add_script(req, 'mastertickets/linkify_blocking.js') return template, content_type
def test_xml(self): self.assertEqual(Markup('0<a>0</a> and <b>0</b> and <c/> and' ' <d class="[\'a\', \'\', \'b\']"' ' more_="[\'a\']"/>'), Markup(xml(0, xml.a(0), ' and ', xml.b(0.0), ' and ', xml.c(None), ' and ', xml.d('', class_=[b'a', b'', b'b'], more__=[b'a']))))
def render_def(s): rendered = s and render_item(s) or None if isinstance(s, str): s = Markup(s.replace('&', '&')) return [ tag.td(rendered, nbsp, style='border:none'), tag.td(tag.kbd(s), style=value_style) ]
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 expand_macro(self, formatter, name, args): if not args: return Markup() config = None if name == self.CONFIG_KEY: lines = args.splitlines() if not lines or not lines[0].startswith('#!'): return Markup() config = self._parse_config([i.strip() for i in lines[0][2:].split(',')]) else: config = self.CONFIG[name] if not config: return Markup() def to_html(text): if not text: return '' return Markup('<br>'.join([format_to_oneliner(self.env, formatter.context, line) \ for line in text.splitlines()])) def has_keys(dict, keys): for key in keys: if dict.has_key(key): return True return False rows = self.parse_doc(args) if not rows: return Markup() seen = [] for desc, keys in config: if [row for row in rows if has_keys(row, keys)]: seen.append(desc) thead = tag.thead() for desc, keys in config: if not desc in seen: continue thead(tag.td(tag.b(desc))) tbody = tag.tbody() for row in rows: trow = tag.tr() for desc, keys in config: if not desc in seen: continue tcol = tag.td() for key in keys: if row.has_key(key): tcol(to_html(row[key])) trow(tcol) tbody(trow) return tag.table([thead, tbody], class_='wiki')
def test_sanitize_remove_script_elem(self): markup = Markup('<script>alert("Foo")</script>') self.assertEquals('', markup.sanitize()) markup = Markup('<SCRIPT SRC="http://example.com/"></SCRIPT>') self.assertEquals('', markup.sanitize()) markup = Markup('<SCR\0IPT>alert("foo")</SCR\0IPT>') self.assertRaises(HTMLParseError, markup.sanitize) markup = Markup('<SCRIPT&XYZ SRC="http://example.com/"></SCRIPT>') self.assertRaises(HTMLParseError, markup.sanitize)
def end_process(self, numrows): notmodifiedcount = numrows - len(self.added) - len(self.modified) self.message = 'Scroll to see a preview of the tickets as they will be imported. If the data is correct, select the \'\'\'Execute Import\'\'\' button.\n' + ' * ' + str( numrows) + ' tickets will be imported (' + str(len( self.added)) + ' added, ' + str(len( self.modified)) + ' modified, ' + str( notmodifiedcount) + ' unchanged).\n' + self.message self.data['message'] = Markup(self.styles) + wiki_to_html( self.message, self.env, self.req) + Markup('<br/>') return 'import_preview.html', self.data, None
def _render_macro_FaceImg(self, req, content): uid = content rn = self.get_realname(uid) if not rn: raise Exception("unknown user") if self._jpegPhoto_width: return Markup( html.IMG(src=req.href.user("%s/jpegPhoto" % uid), alt=rn, width=self._jpegPhoto_width)) return Markup(html.IMG(src=req.href.user("%s/jpegPhoto" % uid), alt=rn))
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 get_timeline_events(self, req, start, stop, filters): if 'changeset' in filters: format = req.args.get('format') wiki_format = self.wiki_format_messages show_files = self.timeline_show_files db = self.env.get_db_cnx() repos = self.env.get_repository(req.authname) for chgset in repos.get_changesets(start, stop): message = chgset.message or '--' if wiki_format: shortlog = wiki_to_oneliner(message, self.env, db, shorten=True) else: shortlog = shorten_line(message) if format == 'rss': title = Markup('Changeset [%s]: %s', chgset.rev, shortlog) href = req.abs_href.changeset(chgset.rev) if wiki_format: message = wiki_to_html(message, self.env, req, db, absurls=True) else: message = html.PRE(message) else: title = Markup('Changeset <em>[%s]</em> by %s', chgset.rev, chgset.author) href = req.href.changeset(chgset.rev) if wiki_format: if self.timeline_long_messages: message = wiki_to_html(message, self.env, req, db, absurls=True) else: message = wiki_to_oneliner(message, self.env, db, shorten=True) else: message = shortlog if show_files and req.perm.has_permission('BROWSER_VIEW'): files = [] for chg in chgset.get_changes(): if show_files > 0 and len(files) >= show_files: files.append(html.LI(Markup('…'))) break files.append(html.LI(html.DIV(class_=chg[2]), chg[0] or '/')) message = html.UL(files, class_="changes") + message yield 'changeset', href, title, chgset.date, chgset.author,\ message
def end_process(self, numrows): self.message = 'Scroll to see a preview of the tickets as they will be imported. If the data is correct, select the \'\'\'Execute Import\'\'\' button.\n' + ' * ' + str( numrows ) + ' tickets will be imported (' + str(self.added) + ' added, ' + str( self.modifiedcount) + ' modified, ' + str( self.notmodifiedcount) + ' unchanged).\n' + self.message self.req.hdf['report.description'] = Markup( self.styles ) + wiki_to_html(self.message, self.env, self.req) + Markup( '<br/><form action="importer" method="post"><input type="hidden" name="action" value="import" /><div class="buttons"><input type="submit" name="cancel" value="Cancel" /><input type="submit" value="Execute import" /></div></form>' ) self.req.hdf['report.numrows'] = numrows self.req.hdf['report.mode'] = 'list' return 'report.cs', None
def wiki_to_oneliner(env, wikitext, path, basepath, db=None, shorten=False, absurls=False, req=None): if not wikitext: return Markup() out = StringIO() context = Context.from_request(req, absurls=False) OneLinerTexFormatter(env, context, path, basepath).format(wikitext, out, shorten) return Markup(out.getvalue())
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 = [] for milestone in Milestone.select(self.env, include_completed=False): if include.match( milestone.name): # and not exclude.match(milestone.name): milestones.append(milestone) 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), milestone.name)) out.write('</ul>\n') return Markup(out.getvalue())
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 test_sanitize_remove_style_scripts(self): # Inline style with url() using javascript: scheme markup = Markup('<DIV STYLE=\'background: url(javascript:alert("foo"))\'>') self.assertEquals('<div>', markup.sanitize()) # Inline style with url() using javascript: scheme, using control char markup = Markup('<DIV STYLE=\'background: url(javascript:alert("foo"))\'>') self.assertEquals('<div>', markup.sanitize()) # Inline style with url() using javascript: scheme, in quotes markup = Markup('<DIV STYLE=\'background: url("javascript:alert(foo)")\'>') self.assertEquals('<div>', markup.sanitize()) # IE expressions in CSS not allowed markup = Markup('<DIV STYLE=\'width: expression(alert("foo"));\'>') self.assertEquals('<div>', markup.sanitize()) markup = Markup('<DIV STYLE=\'background: url(javascript:alert("foo"));' 'color: #fff\'>') self.assertEquals('<div style="color: #fff">', markup.sanitize())
def test_bad_check(self): class BadRegistrationInspector(GenericRegistrationInspector): """Child class, that is left as a pure copy of its base. Bad check class example, because check method is not implemented. """ check = BadRegistrationInspector(self.env) # Default (empty) response for providing additional fields is safe. field_res = check.render_registration_fields(self.req, {}) self.assertEqual(len(field_res), 2) self.assertEqual((Markup(field_res[0]), field_res[1]), (Markup(''), {})) # Check class without 'validate_registration' implementation fails. self.assertRaises(NotImplementedError, check.validate_registration, self.req)
def _render_full_format(self, formatter, post_list, post_instances, heading, max_size, show_meta): """ Renters full blog posts. """ out = '' if 'BLOG_VIEW' in formatter.req.perm('blog'): out = tag.div(class_="blog") out.append( tag.div(tag.a(heading, href=formatter.req.href.blog()), class_="blog-list-title")) for post in post_instances: data = { 'post': post, 'blog_personal_blog': self.config.getbool('fullblog', 'personal_blog'), 'list_mode': True, 'show_meta': show_meta, 'execute_blog_macro': True } if max_size: data['blog_max_size'] = max_size txt = Chrome(self.env).render_template( formatter.req, 'fullblog_macro_post.html', data, {'fragment': True}) out.append(Markup(to_unicode(txt))) return out
class SQLScalar(WikiMacroBase): """Output a number from a scalar (1x1) SQL query. Examples: {{{ {{{ #!SQLScalar SELECT count(id) as 'Number of Tickets' FROM ticket }}} }}} """ # IWikiMacroBase methods def expand_macro(self, formatter, name, content): db = self.env.get_db_cnx() cursor = db.cursor() try: cursor.execute(content) except Exception, e: return system_message(_("Invalid SQL"), exception_to_unicode(e)) value = "Unknown" for row in cursor: value = unicode(row[0]) break out = StringIO() print >> out, u"<span class='wikiscalar'>%s</span>" % value add_stylesheet(formatter.req, 'wikitable/css/wikitable.css') return Markup(out.getvalue())
def change_sid(self, new_sid): assert self.req.authname == 'anonymous', \ 'Cannot change ID of authenticated session' assert new_sid, 'Session ID cannot be empty' if new_sid == self.sid: return db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT sid FROM session WHERE sid=%s", (new_sid, )) if cursor.fetchone(): raise TracError( Markup( 'Session "%s" already exists.<br />' 'Please choose a different session ID.', new_sid), 'Error renaming session') self.env.log.debug('Changing session ID %s to %s' % (self.sid, new_sid)) cursor.execute( "UPDATE session SET sid=%s WHERE sid=%s " "AND authenticated=0", (new_sid, self.sid)) cursor.execute( "UPDATE session_attribute SET sid=%s " "WHERE sid=%s and authenticated=0", (new_sid, self.sid)) db.commit() self.sid = new_sid self.bake_cookie()
def render_macro(self, req, name, content): self.view = "" self.render(req) return Markup(self.view)
def render_macro(self, req, name, content): if not (req.perm.has_permission('TICKET_VIEW') or req.perm.has_permission('TICKET_VIEW_CC')): raise TracError('TICKET_VIEW or TICKET_VIEW_CC permission required') options = copy.copy(DEFAULT_OPTIONS) if content: for arg in content.split(','): i = arg.index('=') options[arg[:i].strip()] = arg[i+1:].strip() db = self.env.get_db_cnx() options = parse_options(db, options) milestone = options['milestone'] cursor = db.cursor() cursor.execute("SELECT owner, p.value " " FROM ticket t, ticket_custom p" " WHERE p.ticket = t.id and p.name = %s" " AND t.milestone = %s", [self.estimation_field, milestone]) sum = 0.0 estimations = {} for owner, estimation in cursor: try: sum += float(estimation) if estimations.has_key(owner): estimations[owner] += float(estimation) else: estimations[owner] = float(estimation) except: pass estimations_string = [] labels = [] for owner, estimation in estimations.iteritems(): labels.append("%s %sh" % (owner, str(int(estimation)))) estimations_string.append(str(int(estimation))) # Title title = 'Workload' # calculate remaining work time if options.get('today') and options.get('enddate'): currentdate = options['today'] day = timedelta(days=1) days_remaining = 0 while currentdate <= options['enddate']: if currentdate.weekday() < 5: days_remaining += 1 currentdate += day title += ' %sh (%s workdays left)' % (int(sum), days_remaining) return Markup("<img src=\"http://chart.apis.google.com/chart?" "chs=%sx%s" "&chd=t:%s" "&cht=p3" "&chtt=%s" "&chl=%s" "&chco=%s\" " "alt=\'Workload Chart\' />" % (options['width'], options['height'], ",".join(estimations_string), title, "|".join(labels), options['color']))
def get_timeline_events(self, req, start, stop, filters): if 'milestone' in filters: format = req.args.get('format') db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute( "SELECT completed,name,description FROM milestone " "WHERE completed>=%s AND completed<=%s", ( start, stop, )) for completed, name, description in cursor: title = Markup('Milestone <em>%s</em> completed', name) if format == 'rss': href = req.abs_href.milestone(name) message = wiki_to_html(description, self.env, req, db, absurls=True) else: href = req.href.milestone(name) message = wiki_to_oneliner(description, self.env, db, shorten=True) yield 'milestone', href, title, completed, None, message or '--'
def get_info(self, event_providers): # TODO: There are supposed benefits to switching from a XML to JSON event source # http://simile.mit.edu/wiki/JSON_event_source:_use_js_Date%28%29_objects self.req.hdf['event_url'] = self.req.href.requirement() # TODO: This needs to be changed to a local source self.req.hdf['event_section'] = 'timeline' # TODO: This needs to be changed to a local source events = [] start = 0 stop = time.time() filters = ['milestone', 'ticket', 'changeset', 'wiki', 'requirement'] for event_provider in event_providers: #try: for kind, href, title, date, author, message in event_provider.get_timeline_events(self.req, start, stop, filters): # TODO: revisit this code... string escaping is still an issue # Strip/escape HTML markup #if isinstance(title, Markup): # title = title.plaintext(keeplinebreaks=False) if not isinstance(title, Markup): title = Markup(title) title = title.plaintext(keeplinebreaks=False) #title = title.stripentities() message = to_unicode(message) events.append({ # Everything but data will be inserted as attributes into an event tag # Data is provided to allow customization of the bubble content # For more information about the XML format, see # http://simile.mit.edu/wiki/How_to_Create_Event_Source_Files 'isDuration': 'false', 'start': time.strftime("%a %b %d %Y %H:%M:%S "+self.get_gmtstring(), time.gmtime(date)), 'title': title, 'data': {'kind': kind, 'title': title, 'href': href, 'author': author or 'anonymous', 'date': format_date(date), 'time': format_time(date, '%H:%M'), 'dateuid': int(date), 'message': message}, 'debug': isinstance(title, Markup), }) #except Exception, e: # cope with a failure of that provider # pass self.req.hdf['events'] = events
def trac_get_reference(rawtext, target, text): fulltext = text and target+' '+text or target link = wiki_to_link(fulltext, self.env, req) uri = None missing = False if isinstance(link, Element): linktext = Markup(link).striptags() # the following is a bit hackish, but it takes into account: # - an eventual trailing '?' for missing wiki pages # - space eventually introduced due to split_page_names option if linktext.rstrip('?').replace(' ', '') != target: text = linktext uri = link.attr.get('href', '') missing = 'missing' in link.attr.get('class_', '') else: uri = req.href.wiki(target) missing = not WikiSystem(self.env).has_page(target) if uri: reference = nodes.reference(rawtext, text or target) reference['refuri']= uri if missing: reference.set_class('missing') return reference return None
def get_changes(env, repos, revs, full=None, req=None, format=None): db = env.get_db_cnx() changes = {} for rev in revs: try: changeset = repos.get_changeset(rev) except NoSuchChangeset: changes[rev] = {} continue wiki_format = env.config['changeset'].getbool('wiki_format_messages') message = changeset.message or '--' absurls = (format == 'rss') if wiki_format: shortlog = wiki_to_oneliner(message, env, db, shorten=True, absurls=absurls) else: shortlog = Markup.escape(shorten_line(message)) if full: if wiki_format: message = wiki_to_html(message, env, req, db, absurls=absurls, escape_newlines=True) else: message = html.PRE(message) else: message = shortlog if format == 'rss': if isinstance(shortlog, Markup): shortlog = shortlog.plaintext(keeplinebreaks=False) message = unicode(message) changes[rev] = { 'date_seconds': changeset.date, 'date': format_datetime(changeset.date), 'age': pretty_timedelta(changeset.date), 'author': changeset.author or 'anonymous', 'message': message, 'shortlog': shortlog, } return changes
def test_sanitize_remove_onclick_attr(self): markup = Markup('<div onclick=\'alert("foo")\' />') self.assertEquals('<div></div>', markup.sanitize())
def test_sanitize_invalid_entity(self): markup = Markup('&junk;') self.assertEquals('&junk;', markup.sanitize())
def test_sanitize_close_empty_tag(self): markup = Markup('<a href="#">fo<br>o</a>') self.assertEquals('<a href="#">fo<br />o</a>', markup.sanitize())
def test_sanitize_remove_src_javascript(self): markup = Markup('<img src=\'javascript:alert("foo")\'>') self.assertEquals('<img />', markup.sanitize()) # Case-insensitive protocol matching markup = Markup('<IMG SRC=\'JaVaScRiPt:alert("foo")\'>') self.assertEquals('<img />', markup.sanitize()) # Grave accents (not parsed) markup = Markup('<IMG SRC=`javascript:alert("RSnake says, \'foo\'")`>') self.assertRaises(HTMLParseError, markup.sanitize) # Protocol encoded using UTF-8 numeric entities markup = Markup('<IMG SRC=\'javascri' 'pt:alert("foo")\'>') self.assertEquals('<img />', markup.sanitize()) # Protocol encoded using UTF-8 numeric entities without a semicolon # (which is allowed because the max number of digits is used) markup = Markup('<IMG SRC=\'java' 'script' ':alert("foo")\'>') self.assertEquals('<img />', markup.sanitize()) # Protocol encoded using UTF-8 numeric hex entities without a semicolon # (which is allowed because the max number of digits is used) markup = Markup('<IMG SRC=\'javascri' 'pt:alert("foo")\'>') self.assertEquals('<img />', markup.sanitize()) # Embedded tab character in protocol markup = Markup('<IMG SRC=\'jav\tascript:alert("foo");\'>') self.assertEquals('<img />', markup.sanitize()) # Embedded tab character in protocol, but encoded this time markup = Markup('<IMG SRC=\'jav	ascript:alert("foo");\'>') self.assertEquals('<img />', markup.sanitize())
def test_unescape_markup(self): string = '<b>"&"</b>' markup = Markup.escape(string) assert isinstance(markup, Markup) self.assertEquals(string, unescape(markup))
def test_sanitize_unchanged(self): markup = Markup('<a href="#">fo<br />o</a>') self.assertEquals('<a href="#">fo<br />o</a>', markup.sanitize())
def test_sanitize_escape_attr(self): markup = Markup('<div title="<foo>"></div>') self.assertEquals('<div title="<foo>"></div>', markup.sanitize())
def test_sanitize_entityref_text(self): markup = Markup('<a href="#">foö</a>') self.assertEquals(u'<a href="#">foö</a>', markup.sanitize())
def test_sanitize_escape_text(self): markup = Markup('<a href="#">fo&</a>') self.assertEquals('<a href="#">fo&</a>', markup.sanitize()) markup = Markup('<a href="#"><foo></a>') self.assertEquals('<a href="#"><foo></a>', markup.sanitize())