def notify(self, resid, subject, author=None): self.subject = subject config = self.config['notification'] if not config.getbool('smtp_enabled'): return from_email, from_name = '', '' if author and config.getbool('smtp_from_author'): from_email = self.get_smtp_address(author) if from_email: from_name = self.name_map.get(author, '') if not from_name: mo = self.longaddr_re.search(author) if mo: from_name = mo.group(1) if not from_email: from_email = config.get('smtp_from') from_name = config.get('smtp_from_name') or self.env.project_name self.replyto_email = config.get('smtp_replyto') self.from_email = from_email or self.replyto_email self.from_name = from_name if not self.from_email and not self.replyto_email: message = tag( tag.p(_('Unable to send email due to identity crisis.')), # convert explicitly to `Fragment` to avoid breaking message # when passing `LazyProxy` object to `Fragment` tag.p(to_fragment(tag_( "Neither %(from_)s nor %(reply_to)s are specified in the " "configuration.", from_=tag.strong("[notification] smtp_from"), reply_to=tag.strong("[notification] smtp_replyto"))))) raise TracError(message, _("SMTP Notification Error")) Notify.notify(self, resid)
def _format_reminder(self, req, ticket, id, time, author, origin, description, delete_button=True): now = to_datetime(None) time = to_datetime(time) if now >= time: when = tag(tag.strong("Right now"), " (pending)") else: when = tag("In ", tag.strong(pretty_timedelta(time)), " (", format_date(time), ")") if description: context = Context.from_request(req, ticket.resource) desc = tag.div(format_to_oneliner(self.env, context, description), class_="description") else: desc = tag() return tag( self._reminder_delete_form(req, id) if delete_button else None, when, " - added by ", tag.em(Chrome(self.env).authorinfo(req, author)), " ", tag.span(pretty_timedelta(origin), title=format_datetime( origin, req.session.get('datefmt', 'iso8601'), req.tz)), " ago.", desc)
def moreless(text, length): """Turns `text` into HTML code where everything behind `length` can be uncovered using a ''show more'' link and later covered again with a ''show less'' link.""" try: if len(text) <= length: return tag(text) except: return tag(text) return tag(tag.span(text[:length]),tag.a(' [', tag.strong(Markup('…')), ']', class_="more"), tag.span(text[length:],class_="moretext"),tag.a(' [', tag.strong('-'), ']', class_="less"))
def moreless(text, length): """Turns `text` into HTML code where everything behind `length` can be uncovered using a ''show more'' link and later covered again with a ''show less'' link.""" try: if len(text) <= length: return tag(text) except: return tag(text) return tag(tag.span(text[:length]), tag.a(' [', tag.strong(Markup('…')), ']', class_="more"), tag.span(text[length:], class_="moretext"), tag.a(' [', tag.strong('-'), ']', class_="less"))
def generate_prefix(prefix): intertrac = intertracs[prefix] if isinstance(intertrac, basestring): yield tag.tr(tag.td(tag.strong(prefix)), tag.td(tag_("Alias for %(name)s", name=tag.strong(intertrac)))) else: url = intertrac.get('url', '') if url: title = intertrac.get('title', url) yield tag.tr(tag.td(tag.a(tag.strong(prefix), href=url + '/timeline')), tag.td(tag.a(title, href=url)))
def generate_prefix(prefix): intertrac = intertracs[prefix] if isinstance(intertrac, basestring): yield tag.tr(tag.td(tag.strong(prefix)), tag.td('Alias for ', tag.strong(intertrac))) else: url = intertrac.get('url', '') if url: title = intertrac.get('title', url) yield tag.tr( tag.td( tag.a(tag.strong(prefix), href=url + '/timeline')), tag.td(tag.a(title, href=url)))
def _format_reminder(self, req, ticket, id, time, author, origin, description, delete_button=True): now = to_datetime(None) time = to_datetime(time) if now >= time: when = tag(tag.strong("Right now"), " (pending)") else: when = tag("In ", tag.strong(pretty_timedelta(time)), " (", format_date(time), ")") if description: context = Context.from_request(req, ticket.resource) desc = tag.div(format_to_oneliner(self.env, context, description), class_="description") else: desc = tag() return tag(self._reminder_delete_form(req, id) if delete_button else None, when, " - added by ", tag.em(Chrome(self.env).authorinfo(req, author)), " ", tag.span(pretty_timedelta(origin), title=format_datetime(origin, req.session.get('datefmt', 'iso8601'), req.tz)), " ago.", desc)
def render_group(group): return tag.ul( tag.li(isinstance(elt, tuple) and tag(tag.strong(elt[0]), render_group(elt[1])) or tag.a(wiki.format_page_name(elt), href=formatter.href.wiki(elt))) for elt in group)
def _validate(self, req, page): valid = True # Validate page size if len(req.args.get('text', '')) > self.max_size: add_warning( req, _( 'The wiki page is too long (must be less ' 'than %(num)s characters)', num=self.max_size)) valid = False # Give the manipulators a pass at post-processing the page for manipulator in self.page_manipulators: for field, message in manipulator.validate_wiki_page(req, page): valid = False if field: add_warning( req, tag_( "The Wiki page field %(field)s" " is invalid: %(message)s", field=tag.strong(field), message=message)) else: add_warning( req, tag_("Invalid Wiki page: %(message)s", message=message)) return valid
def _provider_failure(self, exc, req, ep, current_filters, all_filters): """Raise a TracError exception explaining the failure of a provider. At the same time, the message will contain a link to the timeline without the filters corresponding to the guilty event provider `ep`. """ self.log.error("Timeline event provider failed: %s", exception_to_unicode(exc, traceback=True)) ep_kinds = dict((f[0], f[1]) for f in ep.get_timeline_filters(req) or []) ep_filters = set(ep_kinds.keys()) current_filters = set(current_filters) other_filters = set(current_filters) - ep_filters if not other_filters: other_filters = set(all_filters) - ep_filters args = [(a, req.args.get(a)) for a in ('from', 'format', 'max', 'daysback')] href = req.href.timeline(args + [(f, 'on') for f in other_filters]) # TRANSLATOR: ...want to see the 'other kinds of events' from... (link) other_events = tag.a(_('other kinds of events'), href=href) raise TracError(tag( tag.p(tag_("Event provider %(name)s failed for filters " "%(kinds)s: ", name=tag.code(ep.__class__.__name__), kinds=', '.join('"%s"' % ep_kinds[f] for f in current_filters & ep_filters)), tag.strong(exception_to_unicode(exc)), class_='message'), tag.p(tag_("You may want to see the %(other_events)s from the " "Timeline or notify your Trac administrator about the " "error (detailed information was written to the log).", other_events=other_events))))
def format_change(field,oldvalue,newvalue): """Formats tickets changes.""" fieldtag = tag.strong(field) if field == 'cc': oldvalues = set(oldvalue and oldvalue.split(', ') or []) newvalues = set(newvalue and newvalue.split(', ') or []) added = newvalues.difference(oldvalues) removed = oldvalues.difference(newvalues) strng = fieldtag if added: strng += tag(" ", tag.em(', '.join(added)), _(" added")) if removed: if added: strng += tag(', ') strng += tag(" ", tag.em(', '.join(removed)), _(" removed")) return strng elif field == 'description': return fieldtag + tag(_(" modified"), " (", tag.a(_("diff"), href=req.href('ticket',id,action='diff', version=self.commentnum)), ")") elif field == 'comment': self.commentnum = oldvalue self.comment = newvalue return tag("") elif not oldvalue: return fieldtag + tag(" ", tag.em(newvalue), _(" added")) elif not newvalue: return fieldtag + tag(" ", tag.em(oldvalue), _(" deleted")) else: return fieldtag + tag(_(" changed from "), tag.em(oldvalue), _(" to "), tag.em(newvalue))
def render_group(group): return tag.ul( tag.li(tag(tag.strong(elt[0].strip('/')), render_group(elt[1])) if isinstance(elt, tuple) else tag.a(wiki.format_page_name(omitprefix(elt)), href=formatter.href.wiki(elt))) for elt in group)
def _render_source(self, context, stream, annotations, marks=None): from trac.web.chrome import add_warning annotators, labels, titles = {}, {}, {} for annotator in self.annotators: atype, alabel, atitle = annotator.get_annotation_type() if atype in annotations: labels[atype] = alabel titles[atype] = atitle annotators[atype] = annotator annotations = [a for a in annotations if a in annotators] if isinstance(stream, list): stream = HTMLParser(StringIO(u'\n'.join(stream))) elif isinstance(stream, unicode): text = stream def linesplitter(): for line in text.splitlines(True): yield TEXT, line, (None, -1, -1) stream = linesplitter() annotator_datas = [] for a in annotations: annotator = annotators[a] try: data = (annotator, annotator.get_annotation_data(context)) except TracError, e: self.log.warning("Can't use annotator '%s': %s", a, e.message) add_warning(context.req, tag.strong( tag_("Can't use %(annotator)s annotator: %(error)s", annotator=tag.em(a), error=tag.pre(e.message)))) data = (None, None) annotator_datas.append(data)
def filter_stream(self, req, method, filename, stream, data): """Return a filtered Genshi event stream, or the original unfiltered stream if no match. `req` is the current request object, `method` is the Genshi render method (xml, xhtml or text), `filename` is the filename of the template to be rendered, `stream` is the event stream and `data` is the data for the current template. See the Genshi documentation for more information. """ if filename == 'ticket.html': ticket = data['ticket'] if ticket.exists: if req.perm.has_permission('SENSITIVE_VIEW'): qry = DBSession().query(CustomerRequest) if not qry.get(ticket.values.get('customerrequest')).active: div = tag.div( tag.div( tag.strong(u'Heads up! '), tag.span(u'This ticket is assigned to an inactive customer request.',), class_="alert alert-info"), id='inactive_cr_ticket') return stream | Transformer("//div[@id='ticket']").before(div) return stream
def process_request(self, req): if req.path_info == '/mindmap/status': db = self.env.get_db_cnx() cursor = db.cursor() try: cursor.execute('SELECT hash,content FROM mindmapcache') content = tag.html( tag.body( tag.dd([[ tag.dt(tag.a(k, href=req.href.mindmap(k + '.mm'))), tag.dd(tag.pre(v)) ] for k, v in cursor.fetchall()]))) except Exception, e: content = tag.html( tag.body(tag.strong("DB Error: " + unicode(e)))) html = content.generate().render("xhtml") req.send_response(200) req.send_header('Cache-control', 'must-revalidate') req.send_header('Content-Type', 'text/html;charset=utf-8') req.send_header('Content-Length', len(html)) req.end_headers() if req.method != 'HEAD': req.write(html) raise RequestDone
def _render_property(self, name, mode, context, props): repos, revs = props[name] def link(rev): chgset = repos.get_changeset(rev) return tag.a(rev, class_="changeset", title=shorten_line(chgset.message), href=context.href.changeset(rev, repos.reponame)) if name == 'Parents' and len(revs) == 2: # merge new = context.resource.id parent_links = [ (link(rev), ' (', tag.a('diff', title=_("Diff against this parent " "(show the changes merged from the other parents)"), href=context.href.changeset(new, repos.reponame, old=rev)), ')') for rev in revs] return tag([(parent, ', ') for parent in parent_links[:-1]], parent_links[-1], tag.br(), tag.span(tag_("Note: this is a %(merge)s changeset, " "the changes displayed below correspond " "to the merge itself.", merge=tag.strong('merge')), class_='hint'), tag.br(), # TODO: only keep chunks present in both parents # (conflicts) or in none (extra changes) # tag.span('No changes means the merge was clean.', # class_='hint'), tag.br(), tag.span(tag_("Use the %(diff)s links above to see all " "the changes relative to each parent.", diff=tag.tt('(diff)')), class_='hint')) return tag([tag(link(rev), ', ') for rev in revs[:-1]], link(revs[-1]))
def expand_macro(self, formatter, name, content, args=None): hint = '|| yyyy-mm-ddThh:mm:ss || yyyy-mm-ddThh:mm:ss || message' pattern = "\s*||(.*)||(.*)||(.*)".replace('|', '\|') pattern = re.compile(pattern) try: if content == None: return tag.div('ShowWhen Macro is not supported. Use WikiProcessor-style instead.', \ class_="system-message") now = datetime.now(utc) for line in content.split('\n'): matched = pattern.match(line) if matched: result = matched.groups() by, to, text = result by, to = parse_date(by, None, hint), parse_date(to, None, hint) self.env.log.debug('parsed time range: %s / %s' % (by, to)) if by <= now and now <= to: return format_to_html(self.env, formatter.context, text) return None except Exception, e: return tag.div(tag.strong(e.title), ': ' + e.message, class_="system-message")
def format_change(field, oldvalue, newvalue): """Formats tickets changes.""" fieldtag = tag.strong(field) if field == "cc": oldvalues = set(oldvalue and oldvalue.split(", ") or []) newvalues = set(newvalue and newvalue.split(", ") or []) added = newvalues.difference(oldvalues) removed = oldvalues.difference(newvalues) strng = fieldtag if added: strng += tag(" ", tag.em(", ".join(added)), " added") if removed: if added: strng += tag(", ") strng += tag(" ", tag.em(", ".join(removed)), " removed") return strng elif field == "description": return fieldtag + tag( " modified (", tag.a("diff", href=href("ticket", id, action="diff", version=self.commentnum)), ")", ) elif field == "comment": self.commentnum = oldvalue self.comment = newvalue return tag("") elif not oldvalue: return fieldtag + tag(" ", tag.em(newvalue), " added") elif not newvalue: return fieldtag + tag(" ", tag.em(oldvalue), " deleted") else: return fieldtag + tag(" changed from ", tag.em(oldvalue), " to ", tag.em(newvalue))
def mozillate(fmt, match, fullmatch): expr = match[1:-1] if match[0] == '*': return tag.strong(expr) elif match[0] == '/': return tag.em(expr) else: return tag.span(expr, class_='underline')
class MacroListMacro(WikiMacroBase): _domain = 'messages' _description = cleandoc_( """Display a list of all installed Wiki macros, including documentation if available. Optionally, the name of a specific macro can be provided as an argument. In that case, only the documentation for that macro will be rendered. Note that this macro will not be able to display the documentation of macros if the `PythonOptimize` option is enabled for mod_python! """) def expand_macro(self, formatter, name, content): from trac.wiki.formatter import system_message content = content.strip() if content else '' name_filter = content.strip('*') def get_macro_descr(): for macro_provider in formatter.wiki.macro_providers: names = list(macro_provider.get_macros() or []) if name_filter and not any(name.startswith(name_filter) for name in names): continue try: name_descriptions = [ (name, macro_provider.get_macro_description(name)) for name in names] except Exception, e: yield system_message( _("Error: Can't get description for macro %(name)s", name=names[0]), e), names else: for descr, pairs in groupby(name_descriptions, key=lambda p: p[1]): if descr: if isinstance(descr, (tuple, list)): descr = dgettext(descr[0], descr[1]) else: descr = to_unicode(descr) or '' if content == '*': descr = format_to_oneliner( self.env, formatter.context, descr, shorten=True) else: descr = format_to_html( self.env, formatter.context, descr) yield descr, [name for name, descr in pairs] return tag.div(class_='trac-macrolist')( (tag.h3(tag.code('[[', names[0], ']]'), id='%s-macro' % names[0]), len(names) > 1 and tag.p(tag.strong(_("Aliases:")), [tag.code(' [[', alias, ']]') for alias in names[1:]]) or None, description or tag.em(_("Sorry, no documentation found"))) for description, names in sorted(get_macro_descr(), key=lambda item: item[1][0]))
def _render_source(self, context, stream, annotations): from trac.web.chrome import add_warning annotators, labels, titles = {}, {}, {} for annotator in self.annotators: atype, alabel, atitle = annotator.get_annotation_type() if atype in annotations: labels[atype] = alabel titles[atype] = atitle annotators[atype] = annotator annotations = [a for a in annotations if a in annotators] if isinstance(stream, list): stream = HTMLParser(StringIO(u'\n'.join(stream))) elif isinstance(stream, unicode): text = stream def linesplitter(): for line in text.splitlines(True): yield TEXT, line, (None, -1, -1) stream = linesplitter() annotator_datas = [] for a in annotations: annotator = annotators[a] try: data = (annotator, annotator.get_annotation_data(context)) except TracError as e: self.log.warning("Can't use annotator '%s': %s", a, e.message) add_warning( context.req, tag.strong( tag_("Can't use %(annotator)s annotator: %(error)s", annotator=tag.em(a), error=tag.pre(e.message)))) data = (None, None) annotator_datas.append(data) def _head_row(): return tag.tr([ tag.th(labels[a], class_=a, title=titles[a]) for a in annotations ] + [tag.th(u'\xa0', class_='content')]) def _body_rows(): for idx, line in enumerate(_group_lines(stream)): row = tag.tr() for annotator, data in annotator_datas: if annotator: annotator.annotate_row(context, row, idx + 1, line, data) else: row.append(tag.td()) row.append(tag.td(line)) yield row return tag.table(class_='code')(tag.thead(_head_row()), tag.tbody(_body_rows()))
def test_find_element_with_tag(self): frag = tag(tag.p('Paragraph with a ', tag.a('link', href='http://www.edgewall.org'), ' and some ', tag.strong('strong text'))) self.assertIsNotNone(find_element(frag, tag='p')) self.assertIsNotNone(find_element(frag, tag='a')) self.assertIsNotNone(find_element(frag, tag='strong')) self.assertIsNone(find_element(frag, tag='input')) self.assertIsNone(find_element(frag, tag='textarea'))
def _render_source(self, context, stream, annotations): from trac.web.chrome import add_warning annotators, labels, titles = {}, {}, {} for annotator in self.annotators: atype, alabel, atitle = annotator.get_annotation_type() if atype in annotations: labels[atype] = alabel titles[atype] = atitle annotators[atype] = annotator annotations = [a for a in annotations if a in annotators] if isinstance(stream, list): stream = HTMLParser(StringIO(u"\n".join(stream))) elif isinstance(stream, unicode): text = stream def linesplitter(): for line in text.splitlines(True): yield TEXT, line, (None, -1, -1) stream = linesplitter() annotator_datas = [] for a in annotations: annotator = annotators[a] try: data = (annotator, annotator.get_annotation_data(context)) except TracError as e: self.log.warning("Can't use annotator '%s': %s", a, e) add_warning( context.req, tag.strong( tag_("Can't use %(annotator)s annotator: %(error)s", annotator=tag.em(a), error=tag.pre(e)) ), ) data = None, None annotator_datas.append(data) def _head_row(): return tag.tr( [tag.th(labels[a], class_=a, title=titles[a]) for a in annotations] + [tag.th(u"\xa0", class_="content")] ) def _body_rows(): for idx, line in enumerate(_group_lines(stream)): row = tag.tr() for annotator, data in annotator_datas: if annotator: annotator.annotate_row(context, row, idx + 1, line, data) else: row.append(tag.td()) row.append(tag.td(line)) yield row return tag.table(class_="code")(tag.thead(_head_row()), tag.tbody(_body_rows()))
def _expand_view_topic(self, formatter, name, content): self.log.debug("Rendering ViewTopic macro...") # Check permission if not formatter.perm.has_permission('DISCUSSION_VIEW'): return # Determine topic subject page_name = formatter.req.path_info[6:] or 'WikiStart' subject = content or page_name # Create request context. context = Context.from_request(formatter.req) context.realm = 'discussion-wiki' # Get database access. db = self.env.get_db_cnx() context.cursor = db.cursor() # Get API component. api = self.env[DiscussionApi] # Get topic by subject try: id = int(subject) topic = api.get_topic(context, id) except: topic = api.get_topic_by_subject(context, subject) self.log.debug('subject: %s' % (subject,)) self.log.debug('topic: %s' % (topic,)) # Prepare request and resource object. if topic: context.req.args['topic'] = topic['id'] context.resource = Resource('discussion', 'topic/%s' % (topic['id'] ,)) # Process discussion request. template, data = api.process_discussion(context) # Return rendered template. data['discussion']['mode'] = 'message-list' data['discussion']['page_name'] = page_name if context.redirect_url: # Generate HTML elements for redirection. href = context.req.href(context.redirect_url[0]) + \ context.redirect_url[1] self.log.debug("Redirecting to %s" % (href)) return tag.div(tag.strong('Redirect: '), ' This page redirects to ', tag.a(href, href = href), tag.script("window.location = '" + context.req.href('discussion', 'redirect', redirect_url = href) + "'", language = "JavaScript"), class_ = "system-message") else: # Render template. return to_unicode(Chrome(self.env).render_template(formatter.req, template, data, 'text/html', True))
def get_powered_by_sign(): return tag.p("Powered by ", tag.a(tag.strong("IttecoTracPlugin %s" % __version__), href="http://tracplugin.itteco.com/"), tag.br(), tag( "By ", tag.a("Itteco Software", href="http://www.itteco.com"), ), class_="left")
def expand_macro(self, formatter, name, content, args): title = 'Color Scheme' classes = 'colormacro' if args and 'title' in args: title = args['title'] if args and 'class' in args: classes += ' ' + args['class'] tbody = [] have_comment = False colors = self._parse_arguments(content) for color in colors: if len(color['title']) > 0: have_comment = True ## Create row tbody.append( [ tag.td()(tag.strong(color['title'])), tag.td( style='background-color:' + color['orig'] )( tag.div(style='color: black')(color['hex']), tag.div(style='color: white')(color['hex']) ), tag.td( style='background-color:' + color['orig'] )( tag.div(style='color: black')(color['rgbp']), tag.div(style='color: white')(color['rgbp']) ), ] ) ## end for loop if len(tbody) > 0: colcount = len(tbody[0]) if not have_comment: colcount -= 1 table = tag.table(class_=classes) table()(tag.thead()(tag.th(colspan='%d' % colcount)(title))) ## Attach row in table. if have_comment: table()(tag.tbody(class_='colorlist')([tag.tr(row) for row in tbody])) else: table()(tag.tbody(class_='colorlist')([tag.tr(row[1:]) for row in tbody])) return table; else: return tag.div(class_='colormacro')('Nothing to display')
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 expand_macro(self, formatter, name, content): """Print redirect notice after edit.""" target = extract_url(self.env, formatter.context, content) if not target: target = formatter.context.req.href.wiki(content) return tag.div(tag.strong('This page redirects to: '), tag.a(content, href=target), class_='system-message', id='notice')
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 render_property_diff(self, name, old_context, old_props, new_context, new_props, options): old, new = old_props[name], new_props[name] # Render as diff only if multiline (see #3002) if '\n' not in old and '\n' not in new: return None unidiff = '--- \n+++ \n' + \ '\n'.join(unified_diff(old.splitlines(), new.splitlines(), options.get('contextlines', 3))) return tag.li('Property ', tag.strong(name), Mimeview(self.env).render(old_context, 'text/x-diff', unidiff))
def expand_macro(self, formatter, name, content): """Print redirect notice after edit.""" target = extract_url(self.env, formatter.context, content) if not target: target = formatter.context.req.href.wiki(content) return tag.div( tag.strong('This page redirects to: '), tag.a(content, href=target), class_ = 'system-message', id = 'notice' )
def expand_macro(self, formatter, name, content): from trac.wiki.formatter import system_message content = content.strip() if content else '' name_filter = content.strip('*') def get_macro_descr(): for macro_provider in formatter.wiki.macro_providers: names = list(macro_provider.get_macros() or []) if name_filter and not any( name.startswith(name_filter) for name in names): continue try: name_descriptions = [ (name, macro_provider.get_macro_description(name)) for name in names ] except Exception as e: yield system_message( _("Error: Can't get description for macro %(name)s", name=names[0]), e), names else: for descr, pairs in groupby(name_descriptions, key=lambda p: p[1]): if descr: if isinstance(descr, (tuple, list)): descr = dgettext(descr[0], to_unicode(descr[1])) \ if descr[1] else '' else: descr = to_unicode(descr) or '' if content == '*': descr = format_to_oneliner(self.env, formatter.context, descr, shorten=True) else: descr = format_to_html(self.env, formatter.context, descr) yield descr, [name for name, descr in pairs] return tag.div(class_='trac-macrolist')( (tag.h3(tag.code('[[', names[0], ']]'), id='%s-macro' % names[0]), len(names) > 1 and tag.p( tag.strong(_("Aliases:")), [tag.code(' [[', alias, ']]') for alias in names[1:]]) or None, description or tag.em(_("Sorry, no documentation found"))) for description, names in sorted(get_macro_descr(), key=lambda item: item[1][0]))
def render_tag_changes(old_tags, new_tags): old_tags = split_into_tags(old_tags or '') new_tags = split_into_tags(new_tags or '') added = sorted(new_tags - old_tags) added = added and \ tagn_("%(tags)s added", "%(tags)s added", len(added), tags=builder.em(', '.join(added))) removed = sorted(old_tags - new_tags) removed = removed and \ tagn_("%(tags)s removed", "%(tags)s removed", len(removed), tags=builder.em(', '.join(removed))) # TRANSLATOR: How to delimit added and removed tags. delim = added and removed and _("; ") return builder(builder.strong(_("Tags")), ' ', added, delim, removed)
def _do_switch(self, env, req, form): data = {} data['page_title'] = get_resource_description(env, form.resource, href=req.href) data['title'] = get_resource_shortname(env, form.resource) data['siblings'] = [] for sibling in form.siblings: form_id = tag.strong(tag.a(_("Form %(form_id)s", form_id=sibling[0]), href=req.href.form(sibling[0]))) if sibling[1] == '': data['siblings'].append(form_id) else: # TRANSLATOR: Form list entry for form select page data['siblings'].append(tag_( "%(form_id)s (subcontext = '%(subcontext)s')", form_id=form_id, subcontext = sibling[1])) add_stylesheet(req, 'tracforms/tracforms.css') return 'switch.html', data, None
def format_change(field, oldvalue, newvalue): """Formats tickets changes.""" fieldtag = tag.strong(field) if field == 'cc': oldvalues = set(oldvalue and oldvalue.split(', ') or []) newvalues = set(newvalue and newvalue.split(', ') or []) added = newvalues.difference(oldvalues) removed = oldvalues.difference(newvalues) strng = fieldtag if added: strng += tag(" ", tag.em(', '.join(added)), " added") if removed: if added: strng += tag(', ') strng += tag(" ", tag.em(', '.join(removed)), " removed") return strng elif field == 'description': return fieldtag + tag( " modified (", tag.a("diff", href=href('ticket', id, action='diff', version=self.commentnum)), ")") elif field == 'comment': self.commentnum = oldvalue self.comment = newvalue return tag("") elif not oldvalue: return fieldtag + tag(" ", tag.em(newvalue), " added") elif not newvalue: return fieldtag + tag(" ", tag.em(oldvalue), " deleted") else: return fieldtag + tag(" changed from ", tag.em(oldvalue), " to ", tag.em(newvalue))
def filter_stream(self, req, method, filename, stream, data): if 'modifiedfiles' in data: numconflictingtickets = self.__process_ticket_request(req, True) #Display a warning message if there are conflicting tickets if numconflictingtickets > 0: if numconflictingtickets == 1: text = " There is one ticket in conflict!" else: text = " There are %s tickets in conflict!" % str(numconflictingtickets) stream |= Transformer("//div[@id='changelog']").before(tag.p(tag.strong("Warning:"), text, style='background: #def; border: 2px solid #00d; padding: 3px;')) #Display the link to this ticket's modifiedfiles page stream |= Transformer("//div[@id='changelog']").before( tag.p( 'Have a look at the ', tag.a("list of modified files", href="../modifiedfiles/" + str(data["modifiedfiles"])), ' related to this ticket.' ) ) return stream
def require_secret(macro, environ, *secrets): """Macro for hiding text that can be shown by submitting a secret. You can provide several secrets as arguments. If ANY of the secret was submitted by the user, the content is shown. Requires the following keys in the environ: ``submitted_secrets`` A set of secrets for this scenario which were submitted by the user. """ show_content = any(x in environ['submitted_secrets'] for x in secrets) if show_content: return macro.parsed_body() else: dragons = tag.strong(_('Here be dragons.')) text = _('This content is hidden, ' 'you need to submit a specific secret in order to show it.') return tag.div(tag.p(dragons, ' ', text), class_='require_secret')
def process_request(self, req): if req.path_info == '/mindmap/status': db = self.env.get_db_cnx() cursor = db.cursor() try: cursor.execute('SELECT hash,content FROM mindmapcache') content = tag.html(tag.body(tag.dd( [ [tag.dt(tag.a(k,href=req.href.mindmap(k + '.mm'))),tag.dd(tag.pre(v))] for k,v in cursor.fetchall()] ))) except Exception, e: content = tag.html(tag.body(tag.strong("DB Error: " + unicode(e)))) html = content.generate().render("xhtml") req.send_response(200) req.send_header('Cache-control', 'must-revalidate') req.send_header('Content-Type', 'text/html;charset=utf-8') req.send_header('Content-Length', len(html)) req.end_headers() if req.method != 'HEAD': req.write(html) raise RequestDone
def _generate_attachmentflags_fieldset(self, readonly=True, current_flags=None, form=False): fields = Fragment() for flag in self.known_flags: flagid = 'flag_' + flag if current_flags and flag in current_flags: date = datetime.datetime.fromtimestamp( current_flags[flag]["updated_on"], utc) text = tag.span( tag.strong(flag), " set by ", tag.em(current_flags[flag]["updated_by"]), ", ", tag.span(pretty_timedelta(date), title=format_datetime(date)), " ago") if readonly == True: fields += tag.input(text, \ type='checkbox', id=flagid, \ name=flagid, checked="checked", disabled="true") + tag.br() else: fields += tag.input(text, \ type='checkbox', id=flagid, \ name=flagid, checked="checked") + tag.br() else: if readonly == True: fields += tag.input(flag, \ type='checkbox', id=flagid, \ name=flagid, disabled="true") + tag.br() else: fields += tag.input(flag, \ type='checkbox', id=flagid, \ name=flagid) + tag.br() if form and not readonly: return tag.form(tag.fieldset( tag.legend("Attachment Flags") + fields, tag.input(type="hidden", name="action", value="update_flags"), tag.input(type="submit", value="Update flags")), method="POST") return tag.fieldset(tag.legend("Attachment Flags") + fields)
def create_wiki_page(self, name=None, content=None, comment=None): """Creates a wiki page, with a random unique CamelCase name if none is provided, random content if none is provided and a random comment if none is provided. Returns the name of the wiki page. """ if name is None: name = random_unique_camel() if content is None: content = random_page() self.go_to_wiki(name) tc.find("The page %s does not exist." % tag.strong(name)) self.edit_wiki_page(name, content, comment) # verify the event shows up in the timeline self.go_to_timeline() tc.formvalue('prefs', 'wiki', True) tc.submit() tc.find(name + ".*created") self.go_to_wiki(name) return name
def _validate(self, req, page): valid = True # Validate page size if len(req.args.get('text', '')) > self.max_size: add_warning(req, _("The wiki page is too long (must be less " "than %(num)s characters)", num=self.max_size)) valid = False # Give the manipulators a pass at post-processing the page for manipulator in self.page_manipulators: for field, message in manipulator.validate_wiki_page(req, page): valid = False if field: add_warning(req, tag_("The Wiki page field %(field)s" " is invalid: %(message)s", field=tag.strong(field), message=message)) else: add_warning(req, tag_("Invalid Wiki page: %(message)s", message=message)) return valid
def _post_process_request_history(self, req, data): history = [] page_histories = data.get('history', []) resource = data['resource'] tags_histories = tag_changes(self.env, resource) for page_history in page_histories: while tags_histories and \ tags_histories[0][0] >= page_history['date']: tags_history = tags_histories.pop(0) date = tags_history[0] author = tags_history[1] old_tags = split_into_tags(tags_history[2] or '') new_tags = split_into_tags(tags_history[3] or '') added = sorted(new_tags - old_tags) added = added and \ tagn_("%(tags)s added", "%(tags)s added", len(added), tags=tag.em(', '.join(added))) removed = sorted(old_tags - new_tags) removed = removed and \ tagn_("%(tags)s removed", "%(tags)s removed", len(removed), tags=tag.em(', '.join(removed))) # TRANSLATOR: How to delimit added and removed tags. delim = added and removed and _("; ") comment = tag(tag.strong(_("Tags")), ' ', added, delim, removed) url = req.href(resource.realm, resource.id, version=page_history['version'], tags_version=to_utimestamp(date)) history.append({'version': '*', 'url': url, 'date': date, 'author': author, 'comment': comment, 'ipnr': ''}) history.append(page_history) data.update(dict(history=history, wiki_to_oneliner=self._wiki_to_oneliner))
def _do_save(self, req, attachment): req.perm(attachment.resource).require('ATTACHMENT_CREATE') parent_resource = attachment.resource.parent if 'cancel' in req.args: req.redirect(get_resource_url(self.env, parent_resource, req.href)) upload = req.args.getfirst('attachment') if not hasattr(upload, 'filename') or not upload.filename: raise TracError(_("No file uploaded")) if hasattr(upload.file, 'fileno'): size = os.fstat(upload.file.fileno())[6] else: upload.file.seek(0, 2) # seek to end of file size = upload.file.tell() upload.file.seek(0) if size == 0: raise TracError(_("Can't upload empty file")) # Maximum attachment size (in bytes) max_size = self.max_size if 0 <= max_size < size: raise TracError( _("Maximum attachment size: %(num)s", num=pretty_size(max_size)), _("Upload failed")) filename = _normalized_filename(upload.filename) if not filename: raise TracError(_("No file uploaded")) # Now the filename is known, update the attachment resource attachment.filename = filename attachment.description = req.args.get('description', '') attachment.author = get_reporter_id(req, 'author') attachment.ipnr = req.remote_addr # Validate attachment valid = True for manipulator in self.manipulators: for field, message in manipulator.validate_attachment( req, attachment): valid = False if field: add_warning( req, tag_( "Attachment field %(field)s is invalid: " "%(message)s", field=tag.strong(field), message=message)) else: add_warning( req, tag_("Invalid attachment: %(message)s", message=message)) if not valid: # Display the attach form with pre-existing data # NOTE: Local file path not known, file field cannot be repopulated add_warning(req, _('Note: File must be selected again.')) data = self._render_form(req, attachment) data['is_replace'] = req.args.get('replace') return data if req.args.get('replace'): try: old_attachment = Attachment(self.env, attachment.resource(id=filename)) if not (req.authname and req.authname != 'anonymous' and old_attachment.author == req.authname) \ and 'ATTACHMENT_DELETE' \ not in req.perm(attachment.resource): raise PermissionError(msg=_( "You don't have permission to " "replace the attachment %(name)s. You can only " "replace your own attachments. Replacing other's " "attachments requires ATTACHMENT_DELETE permission.", name=filename)) if (not attachment.description.strip() and old_attachment.description): attachment.description = old_attachment.description old_attachment.delete() except TracError: pass # don't worry if there's nothing to replace attachment.insert(filename, upload.file, size) req.redirect( get_resource_url(self.env, attachment.resource(id=None), req.href))
def expand_macro(self, formatter, name, content, args=None): t = datetime.now(utc) return tag.strong(format_datetime(t, '%c'))
def enter_secret(macro, environ, *secrets): """Macro for entering a secret. Takes a several secrets as args. Requires the following keys in the environ: ``user`` An instance of :class:`django.contrib.auth.models.User`. Will be used to generate a security token of a secret that is only valid for this user. ``enter_secret_target`` An url for the action attribute of the form element. Submitting the form will generate a POST request with the following data: ``secret`` The secret which was entered by the user ``secret_token`` Occurs several times, one time for each secret that is valid for this form. It is a security token that is build by generating a HMAC with ``settings.SECRET_KEY`` as key. The message is the user id and the valid secret divided by a colon. ``all_secrets`` A list of strings containing all available secrets for this scenario. ``submitted_secrets`` A list of strings containing all secrets submitted by the user for this scenario. ``secret_token_function`` A function that calculates the secret's security token. Takes an user and a secret. ``csrf_token`` Django's CSRF token. Use :func:`django.middleware.csrf.get_token` to get it. """ target = environ['enter_secret_target'] user = environ['user'] # If all secrets are already submitted, change css class solved = all(secret in environ['submitted_secrets'] for secret in secrets) css_class = 'enter_secret secret_solved' if solved else 'enter_secret' secret_div = tag.div(macro.parsed_body(), class_=css_class) if not solved: # If there are no secrets in the arguments, we will accept all secrets if not secrets: secrets = environ['all_secrets'] form = tag.form(method='post', action=target) for secret in secrets: secret_token = environ['secret_token_function'](user, secret) form.append(tag.input(name='secret_token', value=secret_token, type='hidden')) form.append(tag.input(type='hidden', name='csrfmiddlewaretoken', value=environ['csrf_token'])) p = tag.p(tag.strong(_('Enter secret:')), ' ') p.append(tag.input(name='secret', type='text')) p.append(tag.input(type='submit', name='enter_secret', value=_('Submit'))) form.append(p) secret_div.append(form) form_submitted_secrets = [secret for secret in secrets if secret in environ['submitted_secrets']] if form_submitted_secrets: submitted_div = tag.div(tag.p(_('Already submitted secrets:')), _class='submitted_secrets') secret_list = tag.ul() for secret in form_submitted_secrets: secret_list.append(tag.li(secret)) submitted_div.append(secret_list) secret_div.append(submitted_div) return secret_div
def runTest(self): """Test for simple wiki rename""" pagename = self._tester.create_wiki_page() attachment = self._tester.attach_file_to_wiki(pagename) base_url = self._tester.url page_url = base_url + "/wiki/" + pagename def click_rename(): tc.formvalue('rename', 'action', 'rename') tc.submit() tc.url(page_url + r'\?action=rename') tc.find("New name:") tc.go(page_url) tc.find("Rename page") click_rename() # attempt to give an empty new name tc.formvalue('rename-form', 'new_name', '') tc.submit('submit') tc.url(page_url) tc.find("A new name is mandatory for a rename") # attempt to rename the page to an invalid page name tc.formvalue('rename-form', 'new_name', '../WikiStart') tc.submit('submit') tc.url(page_url) tc.find("The new name is invalid") # attempt to rename the page to the current page name tc.formvalue('rename-form', 'new_name', pagename) tc.submit('submit') tc.url(page_url) tc.find("The new name must be different from the old name") # attempt to rename the page to an existing page name tc.formvalue('rename-form', 'new_name', 'WikiStart') tc.submit('submit') tc.url(page_url) tc.find("The page WikiStart already exists") # correct rename to new page name (old page replaced by a redirection) tc.go(page_url) click_rename() newpagename = pagename + 'Renamed' tc.formvalue('rename-form', 'new_name', newpagename) tc.formvalue('rename-form', 'redirect', True) tc.submit('submit') # check redirection page tc.url(page_url) tc.find("See.*/wiki/" + newpagename) tc.find("The page %s has been renamed to %s." % (pagename, newpagename)) tc.find("The page %s has been recreated with a redirect to %s." % (pagename, newpagename)) # check whether attachment exists on the new page but not on old page tc.go(base_url + '/attachment/wiki/' + newpagename + '/' + attachment) tc.notfind("Error: Invalid Attachment") tc.go(base_url + '/attachment/wiki/' + pagename + '/' + attachment) tc.find("Error: Invalid Attachment") # rename again to another new page name (this time, no redirection) tc.go(page_url) click_rename() newpagename = pagename + 'RenamedAgain' tc.formvalue('rename-form', 'new_name', newpagename) tc.formvalue('rename-form', 'redirect', False) tc.submit('submit') tc.url(base_url + "/wiki/" + newpagename) tc.find("The page %s has been renamed to %s." % (pagename, newpagename)) # this time, the original page is gone tc.go(page_url) tc.url(page_url) tc.find("The page %s does not exist" % tag.strong(pagename))