def __get__(self, instance, owner): if instance is None: return self order = ListOption.__get__(self, instance, owner) components = [] implementing_classes = [] for impl in self.xtnpt.extensions(instance): implementing_classes.append(impl.__class__.__name__) if self.include_missing or impl.__class__.__name__ in order: components.append(impl) not_found = sorted(set(order) - set(implementing_classes)) if not_found: raise ConfigurationError( tag_("Cannot find implementation(s) of the %(interface)s " "interface named %(implementation)s. Please check " "that the Component is enabled or update the option " "%(option)s in trac.ini.", interface=tag.code(self.xtnpt.interface.__name__), implementation=tag( (', ' if idx != 0 else None, tag.code(impl)) for idx, impl in enumerate(not_found)), option=tag.code("[%s] %s" % (self.section, self.name)))) def compare(x, y): x, y = x.__class__.__name__, y.__class__.__name__ if x not in order: return int(y in order) if y not in order: return -int(x in order) return cmp(order.index(x), order.index(y)) components.sort(compare) return components
def expand_macro(self, formatter, name, content): from trac.mimeview.api import Mimeview mime_map = Mimeview(self.env).mime_map mime_type_filter = '' args, kw = parse_args(content) if args: mime_type_filter = args.pop(0).strip().rstrip('*') mime_types = {} for key, mime_type in mime_map.iteritems(): if (not mime_type_filter or mime_type.startswith(mime_type_filter)) and key != mime_type: mime_types.setdefault(mime_type, []).append(key) return tag.div(class_='mimetypes')( tag.table(class_='wiki')( tag.thead(tag.tr( tag.th(_("MIME Types")), # always use plural tag.th(tag.a("WikiProcessors", href=formatter.context.href.wiki( 'WikiProcessors'))))), tag.tbody( tag.tr(tag.th(tag.code(mime_type), style="text-align: left"), tag.td(tag.code( ' '.join(sorted(mime_types[mime_type]))))) for mime_type in sorted(mime_types.keys()))))
def send(self, from_addr, recipients, message): # Ensure the message complies with RFC2822: use CRLF line endings message = fix_eol(message, CRLF) self.log.info("Sending notification through SMTP at %s:%d to %s", self.smtp_server, self.smtp_port, recipients) try: server = smtplib.SMTP(self.smtp_server, self.smtp_port) except smtplib.socket.error as e: raise ConfigurationError( tag_("SMTP server connection error (%(error)s). Please " "modify %(option1)s or %(option2)s in your " "configuration.", error=to_unicode(e), option1=tag.code("[notification] smtp_server"), option2=tag.code("[notification] smtp_port"))) # server.set_debuglevel(True) if self.use_tls: server.ehlo() if 'starttls' not in server.esmtp_features: raise TracError(_("TLS enabled but server does not support" " TLS")) server.starttls() server.ehlo() if self.smtp_user: server.login(self.smtp_user.encode('utf-8'), self.smtp_password.encode('utf-8')) start = time.time() resp = sendmail(server, from_addr, recipients, message) t = time.time() - start if t > 5: self.log.warning("Slow mail submission (%.2f s), " "check your mail setup", t) if self.use_tls: # avoid false failure detection when the server closes # the SMTP connection with TLS enabled import socket try: server.quit() except socket.sslerror: pass else: server.quit() msg = email.message_from_string(message) ticket_id = int(msg['x-trac-ticket-id']) msgid = msg['message-id'] aws_re = r'^email-smtp\.([a-z0-9-]+)\.amazonaws\.com$' m = re.match(aws_re, self.smtp_server) if m: parts = resp.split() if len(parts) == 2 and parts[0] == 'Ok': region = m.group(1) msgid = '<%s@%s.amazonses.com>' % (parts[1], region) with self.env.db_transaction as db: cursor = db.cursor() cursor.execute(""" INSERT OR IGNORE INTO messageid (ticket,messageid) VALUES (%s, %s) """, (ticket_id, msgid))
def __get__(self, instance, owner): if instance is None: return self order = ListOption.__get__(self, instance, owner) components = [] implementing_classes = [] for impl in self.xtnpt.extensions(instance): implementing_classes.append(impl.__class__.__name__) if self.include_missing or impl.__class__.__name__ in order: components.append(impl) not_found = sorted(set(order) - set(implementing_classes)) if not_found: raise ConfigurationError( tag_( "Cannot find implementation(s) of the %(interface)s " "interface named %(implementation)s. Please check " "that the Component is enabled or update the option " "%(option)s in trac.ini.", interface=tag.code(self.xtnpt.interface.__name__), implementation=tag( (', ' if idx != 0 else None, tag.code(impl)) for idx, impl in enumerate(not_found)), option=tag.code("[%s] %s" % (self.section, self.name)))) def compare(x, y): x, y = x.__class__.__name__, y.__class__.__name__ if x not in order: return int(y in order) if y not in order: return -int(x in order) return cmp(order.index(x), order.index(y)) components.sort(compare) return components
def _arg_as_int(val, key=None, min=None, max=None): int_val = as_int(val, None, min=min, max=max) if int_val is None: raise MacroError(tag_("Invalid macro argument %(expr)s", expr=tag.code("%s=%s" % (key, val)) if key else tag.code(val))) return int_val
def serializeNode(self, node, indent): rv = tag(" "*indent+"|") if node.type == simpletree.TextNode.type: text = node.value.split("\n") rv.append(tag(tag.code("#text: ", class_=tagClasses["text_marker"]), tag.code(text[0], class_=tagClasses["text"]))) for line in text[1:]: rv.append(tag(tag("\n" + " "*indent+"|"), tag.code(line, class_=tagClasses["text"]))) elif node.type == simpletree.Element.type: rv.append(tag.code(node.name, class_=tagClasses["element"])) if node.attributes: for key, value in node.attributes.iteritems(): rv.append(tag(" ", tag.code(key, class_=tagClasses["attr_name"]), "=", tag.code("\""+value+"\"", class_=tagClasses["attr_value"]))) elif node.type == simpletree.CommentNode.type: rv.append(tag(tag.code("#comment: ", class_=tagClasses["comment_marker"]), tag.code(node.data, class_=tagClasses["comment"]))) elif node.type == simpletree.DocumentType.type: rv.append(tag(tag.code("DOCTYPE: ", class_=tagClasses["doctype_marker"]), tag.code(node.name, class_=tagClasses["doctype"]))) rv.append(tag("\n")) return rv
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 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 expand_macro(self, formatter, name, content): """Render widget contents by re-using wiki markup implementation """ if self.env[DashboardModule] is None: return DashboardModule(self.env).alert_disabled() largs, kwargs = parse_args(content, strict=True) try: (widget_name, ) = largs except ValueError: template = 'widget_alert.html' data = { 'msgtype' : 'error', 'msglabel' : 'Error', 'msgbody' : tag('Expected ', tag.code(1), ' positional argument (i.e. widget name), but got ', tag.code(len(largs)), ' instead'), 'msgdetails' : [ ('Macro name', tag.code('WidgetMacro')), ('Arguments', ', '.join(largs) if largs \ else tag.span('None', class_='label')), ], } else: widget_name = widget_name.strip() wopts = {} wargs = {} def parse_literal(value): try: return literal_eval(value) except (SyntaxError, ValueError): return value for argnm, value in kwargs.iteritems(): if argnm.startswith('wo_'): wopts[argnm[3:]] = value else: wargs[argnm] = parse_literal(value) template = 'widget.html' data = { 'args': wargs, 'bhdb': DashboardChrome(self.env), 'id': None, 'opts': wopts, 'widget': widget_name } return Chrome(self.env).render_template(formatter.req, template, data, fragment=True)
def expand_macro(self, formatter, name, content): from trac.config import ConfigSection, Option section_filter = key_filter = '' args, kw = parse_args(content) if args: section_filter = args.pop(0).strip() if args: key_filter = args.pop(0).strip() def getdoc(option_or_section): doc = to_unicode(option_or_section.__doc__) if doc: doc = dgettext(option_or_section.doc_domain, doc) return doc registry = ConfigSection.get_registry(self.compmgr) sections = dict((name, getdoc(section)) for name, section in registry.iteritems() if name.startswith(section_filter)) registry = Option.get_registry(self.compmgr) options = {} for (section, key), option in registry.iteritems(): if section.startswith(section_filter): options.setdefault(section, {})[key] = option sections.setdefault(section, '') def default_cell(option): default = option.default if default is not None and default != '': return tag.td(tag.code(option.dumps(default)), class_='default') else: return tag.td(_("(no default)"), class_='nodefault') return tag.div(class_='tracini')( (tag.h3(tag.code('[%s]' % section), id='%s-section' % section), format_to_html(self.env, formatter.context, section_doc), tag.table(class_='wiki')(tag.tbody( tag.tr(tag.td(tag.code(option.name)), tag.td( format_to_oneliner(self.env, formatter.context, getdoc(option))), default_cell(option), class_='odd' if idx % 2 else 'even') for idx, option in enumerate( sorted(options.get(section, {}).itervalues(), key=lambda o: o.name)) if option.name.startswith(key_filter)))) for section, section_doc in sorted(sections.iteritems()))
def send(self, from_addr, recipients, message): global local_hostname # Ensure the message complies with RFC2822: use CRLF line endings message = fix_eol(message, CRLF) self.log.info("Sending notification through SMTP at %s:%d to %s", self.smtp_server, self.smtp_port, recipients) try: server = smtplib.SMTP(self.smtp_server, self.smtp_port, local_hostname) local_hostname = server.local_hostname except smtplib.socket.error as e: raise ConfigurationError( tag_( "SMTP server connection error (%(error)s). Please " "modify %(option1)s or %(option2)s in your " "configuration.", error=to_unicode(e), option1=tag.code("[notification] smtp_server"), option2=tag.code("[notification] smtp_port"))) # server.set_debuglevel(True) if self.use_tls: server.ehlo() if 'starttls' not in server.esmtp_features: raise TracError( _("TLS enabled but server does not support" " TLS")) server.starttls() server.ehlo() if self.smtp_user: server.login(self.smtp_user.encode('utf-8'), self.smtp_password.encode('utf-8')) start = time_now() server.sendmail(from_addr, recipients, message) t = time_now() - start if t > 5: self.log.warning( "Slow mail submission (%.2f s), " "check your mail setup", t) if self.use_tls: # avoid false failure detection when the server closes # the SMTP connection with TLS enabled import socket try: server.quit() except socket.sslerror: pass else: server.quit()
def __get__(self, instance, owner): if instance is None: return self value = Option.__get__(self, instance, owner) for impl in self.xtnpt.extensions(instance): if impl.__class__.__name__ == value: return impl raise ConfigurationError( tag_("Cannot find an implementation of the %(interface)s " "interface named %(implementation)s. Please check " "that the Component is enabled or update the option " "%(option)s in trac.ini.", interface=tag.code(self.xtnpt.interface.__name__), implementation=tag.code(value), option=tag.code("[%s] %s" % (self.section, self.name))))
def _render_widget(self, wp, name, ctx, options): """Render widget without failing. """ try : if wp is None : raise InvalidIdentifier("Unknown widget ID") return wp.render_widget(name, ctx, options) except Exception, exc: log_entry = str(uuid4()) exccls = exc.__class__ self.log.exception("- %s - Error rendering widget %s with options %s", log_entry, name, options) data = { 'msgtype' : 'error', 'msglabel' : 'Error', 'msgbody' : _('Exception raised while rendering widget. ' 'Contact your administrator for further details.'), 'msgdetails' : [ ('Widget name', name), ('Exception type', tag.code(exccls.__name__)), ('Log entry ID', log_entry), ], } return 'widget_alert.html', \ { 'title' : _('Widget error'), 'data' : data}, \ ctx
def send(self, from_addr, recipients, message): # Use native line endings in message message = fix_eol(message, os.linesep) self.log.info("Sending notification through sendmail at %s to %s", self.sendmail_path, recipients) cmdline = [self.sendmail_path, '-i', '-f', from_addr] + recipients self.log.debug("Sendmail command line: %s", cmdline) try: child = Popen(cmdline, bufsize=-1, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=close_fds) except OSError as e: raise ConfigurationError( tag_( "Sendmail error (%(error)s). Please modify %(option)s " "in your configuration.", error=to_unicode(e), option=tag.code("[notification] sendmail_path"))) out, err = child.communicate(message) if child.returncode or err: raise Exception("Sendmail failed with (%s, %s), command: '%s'" % (child.returncode, err.strip(), cmdline))
def _prepare_doc_metadata(self, spec): """Transform widget metadata into a format suitable to render documentation. """ return { 'id' : "%s-widget" % (spec['urn'],), 'title' : tag.code(spec['urn']), 'desc' : '\n'.join(l.strip() for l in spec['desc'].splitlines()), 'sections' : [ { 'title' : _('Parameters'), 'entries' : [ { 'caption' : pnm, 'summary' : '\n'.join( l.strip() for l in \ p.get('desc').splitlines()), 'details' : [ ('Type', p.get('type', str)), ('Required', p.get('required', False)), ('Default', p.get('default')), ] } for pnm, p in spec['params'].iteritems()] } ] }
def _invalid_db_str(db_str): return ConfigurationError( tag_( "Invalid format %(db_str)s for the database connection string. " "Please refer to the %(doc)s for help.", db_str=tag.code(db_str), doc=_doc_db_str()))
def expand_macro(self, formatter, name, content): from trac.mimeview.api import Mimeview mime_map = Mimeview(self.env).mime_map mime_type_filter = '' args, kw = parse_args(content) if args: mime_type_filter = args.pop(0).strip().rstrip('*') mime_types = {} for key, mime_type in mime_map.iteritems(): if (not mime_type_filter or mime_type.startswith(mime_type_filter)) and key != mime_type: mime_types.setdefault(mime_type, []).append(key) return tag.div(class_='mimetypes')( tag.table(class_='wiki')( tag.thead(tag.tr( tag.th(_("MIME Types")), # always use plural tag.th(tag.a("WikiProcessors", href=formatter.context.href.wiki( 'WikiProcessors'))))), tag.tbody( tag.tr(tag.th(tag.tt(mime_type), style="text-align: left"), tag.td(tag.code( ' '.join(sorted(mime_types[mime_type]))))) for mime_type in sorted(mime_types.keys()))))
def default_cell(option): default = option.default if default is not None and default != '': return tag.td(tag.code(option.dumps(default)), class_='default') else: return tag.td(_("(no default)"), class_='nodefault')
def process_request(self, req): parent_id = None parent_realm = req.args.get('realm') path = req.args.get('path') filename = None if not parent_realm or not path: raise HTTPBadRequest(_('Bad request')) if parent_realm == 'attachment': raise TracError(tag_("%(realm)s is not a valid parent realm", realm=tag.code(parent_realm))) parent_realm = Resource(parent_realm) action = req.args.get('action', 'view') if action == 'new': parent_id = path.rstrip('/') else: last_slash = path.rfind('/') if last_slash == -1: parent_id, filename = path, '' else: parent_id, filename = path[:last_slash], path[last_slash + 1:] parent = parent_realm(id=parent_id) if not resource_exists(self.env, parent): raise ResourceNotFound( _("Parent resource %(parent)s doesn't exist", parent=get_resource_name(self.env, parent))) # Link the attachment page to parent resource parent_name = get_resource_name(self.env, parent) parent_url = get_resource_url(self.env, parent, req.href) add_link(req, 'up', parent_url, parent_name) add_ctxtnav(req, _('Back to %(parent)s', parent=parent_name), parent_url) if not filename: # there's a trailing '/' if req.args.get('format') == 'zip': self._download_as_zip(req, parent) elif action != 'new': return self._render_list(req, parent) attachment = Attachment(self.env, parent.child(self.realm, filename)) if req.method == 'POST': if action == 'new': data = self._do_save(req, attachment) elif action == 'delete': self._do_delete(req, attachment) else: raise HTTPBadRequest(_("Invalid request arguments.")) elif action == 'delete': data = self._render_confirm_delete(req, attachment) elif action == 'new': data = self._render_form(req, attachment) else: data = self._render_view(req, attachment) add_stylesheet(req, 'common/css/code.css') return 'attachment.html', data, None
def expand_macro(self, formatter, name, args): from trac.config import Option section_filter = key_filter = '' args, kw = parse_args(args) if args: section_filter = args.pop(0).strip() if args: key_filter = args.pop(0).strip() registry = Option.get_registry(self.compmgr) sections = {} for (section, key), option in registry.iteritems(): if section.startswith(section_filter): sections.setdefault(section, {})[key] = option return tag.div(class_='tracini')( (tag.h3(tag.code('[%s]' % section), id='%s-section' % section), tag.table(class_='wiki')( tag.tbody(tag.tr(tag.td(tag.tt(option.name)), tag.td(format_to_oneliner( self.env, formatter.context, to_unicode(option.__doc__)))) for option in sorted(sections[section].itervalues(), key=lambda o: o.name) if option.name.startswith(key_filter)))) for section in sorted(sections))
def short_value(self): if self.is_scalar: if isinstance(self.value, basestring): value = self.value if not isinstance(self.value, unicode): value = unicode(self.value, 'utf-8', 'replace') return tag.q(shorten_line(value, 60)).generate() else: return shorten_line(repr(self.value), 60) elif self.is_collection: if isinstance(self.value, (dict, DictMixin)): return u'{…}' elif isinstance(self.value, list): return u'[…]' elif isinstance(self.value, tuple): return u'(…)' elif isinstance(self.value, set): return u'set([…])' elif isinstance(self.value, frozenset): return u'frozenset([…])' else: try: return tag.code(shorten_line(str(self.value), 60)) except: return '?'
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 _render_widget(self, wp, name, ctx, options): """Render widget without failing. """ if wp is None: data = {'msglabel': _('Warning'), 'msgbody': _('Unknown widget %(name)s', name=name)} return 'widget_alert.html', {'title': '', 'data': data}, ctx try: return wp.render_widget(name, ctx, options) except Exception, exc: log_entry = str(uuid4()) exccls = exc.__class__ self.log.exception( "- %s - Error rendering widget %s with options %s", log_entry, name, options) data = { 'msgtype': 'error', 'msglabel': 'Error', 'msgbody': _('Exception raised while rendering widget. ' 'Contact your administrator for further details.'), 'msgdetails': [ ('Widget name', name), ('Exception type', tag.code(exccls.__name__)), ('Log entry ID', log_entry), ], } return 'widget_alert.html', { 'title': _('Widget error'), 'data': data }, ctx
def expand_macro(self, formatter, name, content): """Render widget contents by re-using wiki markup implementation """ if self.env[DashboardModule] is None: return DashboardModule(self.env).alert_disabled() largs, kwargs = parse_args(content, strict=True) try: (widget_name ,) = largs except ValueError: template = 'widget_alert.html' data = { 'msgtype' : 'error', 'msglabel' : 'Error', 'msgbody' : tag('Expected ', tag.code(1), ' positional argument (i.e. widget name), but got ', tag.code(len(largs)), ' instead'), 'msgdetails' : [ ('Macro name', tag.code('WidgetMacro')), ('Arguments', ', '.join(largs) if largs \ else tag.span('None', class_='label')), ], } else: widget_name = widget_name.strip() wopts = {} ; wargs = {} def parse_literal(value): try: return literal_eval(value) except (SyntaxError, ValueError): return value for argnm, value in kwargs.iteritems(): if argnm.startswith('wo_'): wopts[argnm[3:]] = value else : wargs[argnm] = parse_literal(value) template = 'widget.html' data = { 'args' : wargs, 'bhdb' : DashboardChrome(self.env), 'id' : None, 'opts' : wopts, 'widget' : widget_name } return Chrome(self.env).render_template( formatter.req, template, data, fragment=True)
def serializeNode(self, node, indent): if node.type == simpletree.TextNode.type: if (node.parent.name not in html5lib.constants.rcdataElements and node.parent.name != "plaintext"): value = cgi.escape(node.value, True) else: value = node.value if node.parent.name in ("pre", "textarea"): value = "\n" + value rv = tag.code(value, class_="text") elif node.type == simpletree.Element.type: rv = tag("") rv.append(tag.code("<" + node.name, class_=tagClasses["element"])) if node.attributes: for key, value in node.attributes.iteritems(): value = cgi.escape(value, True) rv.append(tag(" ", tag.code(key, class_=tagClasses["attr_name"]), "=", tag.code("\""+value+"\"", class_=tagClasses["attr_value"]))) rv.append(tag.code(">", class_=tagClasses["element"])) elif node.type == simpletree.CommentNode.type: rv = tag.code("<!--"+node.data+"-->", class_=tagClasses["comment"]) elif node.type == simpletree.DocumentType.type: rv = tag.code("<!DOCTYPE " + node.name + ">", class_=tagClasses["doctype"]) return rv
def expand_macro(self, formatter, name, args): from trac.config import ConfigSection, Option section_filter = key_filter = '' args, kw = parse_args(args) if args: section_filter = args.pop(0).strip() if args: key_filter = args.pop(0).strip() def getdoc(option_or_section): doc = to_unicode(option_or_section.__doc__) if doc: doc = dgettext(option_or_section.doc_domain, doc) return doc registry = ConfigSection.get_registry(self.compmgr) sections = dict((name, getdoc(section)) for name, section in registry.iteritems() if name.startswith(section_filter)) registry = Option.get_registry(self.compmgr) options = {} for (section, key), option in registry.iteritems(): if section.startswith(section_filter): options.setdefault(section, {})[key] = option sections.setdefault(section, '') def default_cell(option): default = option.default if default is True: default = 'true' elif default is False: default = 'false' elif default == 0: default = '0.0' if isinstance(default, float) else '0' elif default: default = ', '.join(to_unicode(val) for val in default) \ if isinstance(default, (list, tuple)) \ else to_unicode(default) else: return tag.td(_("(no default)"), class_='nodefault') return tag.td(tag.code(default), class_='default') return tag.div(class_='tracini')( (tag.h3(tag.code('[%s]' % section), id='%s-section' % section), format_to_html(self.env, formatter.context, section_doc), tag.table(class_='wiki')(tag.tbody( tag.tr( tag.td(tag.tt(option.name)), tag.td( format_to_oneliner(self.env, formatter.context, getdoc(option))), default_cell(option)) for option in sorted(options.get(section, {}).itervalues(), key=lambda o: o.name) if option.name.startswith(key_filter)))) for section, section_doc in sorted(sections.iteritems()))
def __init__(self, path, log=None, params={}): self.cnx = None if path != ':memory:': if not os.access(path, os.F_OK): raise ConfigurationError( _('Database "%(path)s" not found.', path=path)) dbdir = os.path.dirname(path) if not os.access(path, os.R_OK + os.W_OK) or \ not os.access(dbdir, os.R_OK + os.W_OK): raise ConfigurationError( tag_( "The user %(user)s requires read _and_ write permissions " "to the database file %(path)s and the directory it is " "located in.", user=tag.code(getuser()), path=tag.code(path))) self._active_cursors = weakref.WeakKeyDictionary() timeout = int(params.get('timeout', 10.0)) self._eager = params.get('cursor', 'eager') == 'eager' # eager is default, can be turned off by specifying ?cursor= if isinstance(path, unicode): # needed with 2.4.0 path = path.encode('utf-8') cnx = sqlite.connect(path, detect_types=sqlite.PARSE_DECLTYPES, isolation_level=None, check_same_thread=sqlite_version < (3, 3, 1), timeout=timeout) # load extensions extensions = params.get('extensions', []) if len(extensions) > 0: cnx.enable_load_extension(True) for ext in extensions: cnx.load_extension(ext) cnx.enable_load_extension(False) cursor = cnx.cursor() _set_journal_mode(cursor, params.get('journal_mode')) _set_synchronous(cursor, params.get('synchronous')) cursor.close() cnx.isolation_level = 'DEFERRED' ConnectionWrapper.__init__(self, cnx, log)
def send(self, from_addr, recipients, message): # Ensure the message complies with RFC2822: use CRLF line endings message = fix_eol(message, CRLF) self.log.info("Sending notification through SMTP at %s:%d to %s", self.smtp_server, self.smtp_port, recipients) try: server = smtplib.SMTP(self.smtp_server, self.smtp_port) except smtplib.socket.error as e: raise ConfigurationError( tag_("SMTP server connection error (%(error)s). Please " "modify %(option1)s or %(option2)s in your " "configuration.", error=to_unicode(e), option1=tag.code("[notification] smtp_server"), option2=tag.code("[notification] smtp_port"))) # server.set_debuglevel(True) if self.use_tls: server.ehlo() if 'starttls' not in server.esmtp_features: raise TracError(_("TLS enabled but server does not support" " TLS")) server.starttls() server.ehlo() if self.smtp_user: server.login(self.smtp_user.encode('utf-8'), self.smtp_password.encode('utf-8')) start = time.time() server.sendmail(from_addr, recipients, message) t = time.time() - start if t > 5: self.log.warning("Slow mail submission (%.2f s), " "check your mail setup", t) if self.use_tls: # avoid false failure detection when the server closes # the SMTP connection with TLS enabled import socket try: server.quit() except socket.sslerror: pass else: server.quit()
def depart_literal_block(self, node): del self.preserve_space if 'code' in node['classes']: code = node['classes'].index('code') node['classes'].pop(code) code = tag.code(class_=' '.join(node['classes'])) self.context.commit_elem(code, indent=False) del node['classes'] self.default_departure(node) return
def expand_macro(self, formatter, name, args): from trac.config import ConfigSection, Option section_filter = key_filter = '' args, kw = parse_args(args) if args: section_filter = args.pop(0).strip() if args: key_filter = args.pop(0).strip() def getdoc(option_or_section): doc = to_unicode(option_or_section.__doc__) if doc: doc = dgettext(option_or_section.doc_domain, doc) return doc registry = ConfigSection.get_registry(self.compmgr) sections = dict((name, getdoc(section)) for name, section in registry.iteritems() if name.startswith(section_filter)) registry = Option.get_registry(self.compmgr) options = {} for (section, key), option in registry.iteritems(): if section.startswith(section_filter): options.setdefault(section, {})[key] = option sections.setdefault(section, '') def default_cell(option): default = option.default if default is True: default = 'true' elif default is False: default = 'false' elif default == 0: default = '0.0' if isinstance(default, float) else '0' elif default: default = ', '.join(to_unicode(val) for val in default) \ if isinstance(default, (list, tuple)) \ else to_unicode(default) else: return tag.td(_("(no default)"), class_='nodefault') return tag.td(tag.code(default), class_='default') return tag.div(class_='tracini')( (tag.h3(tag.code('[%s]' % section), id='%s-section' % section), format_to_html(self.env, formatter.context, section_doc), tag.table(class_='wiki')(tag.tbody( tag.tr(tag.td(tag.tt(option.name)), tag.td(format_to_oneliner( self.env, formatter.context, getdoc(option))), default_cell(option)) for option in sorted(options.get(section, {}).itervalues(), key=lambda o: o.name) if option.name.startswith(key_filter)))) for section, section_doc in sorted(sections.iteritems()))
def expand_macro(self, formatter, name, args): from trac.config import ConfigSection, Option section_filter = key_filter = '' args, kw = parse_args(args) if args: section_filter = args.pop(0).strip() if args: key_filter = args.pop(0).strip() registry = ConfigSection.get_registry(self.compmgr) sections = dict( (name, dgettext(section.doc_domain, to_unicode(section.__doc__))) for name, section in registry.iteritems() if name.startswith(section_filter)) registry = Option.get_registry(self.compmgr) options = {} for (section, key), option in registry.iteritems(): if section.startswith(section_filter): options.setdefault(section, {})[key] = option sections.setdefault(section, '') return tag.div(class_='tracini')( (tag.h3(tag.code('[%s]' % section), id='%s-section' % section), format_to_html(self.env, formatter.context, section_doc), tag.table(class_='wiki')(tag.tbody( tag.tr( tag.td(tag.tt(option.name)), tag.td( format_to_oneliner( self.env, formatter.context, dgettext(option.doc_domain, to_unicode(option.__doc__)))), tag.td( tag.code(option.default or 'false') if option.default or option.default is False else _("(no default)"), class_='default' if option.default or option.default is False else 'nodefault')) for option in sorted(options.get(section, {}).itervalues(), key=lambda o: o.name) if option.name.startswith(key_filter)))) for section, section_doc in sorted(sections.iteritems()))
def _get_valid_default_handler(self, req): # Use default_handler from the Session if it is a valid value. name = req.session.get('default_handler') handler = self._request_handlers.get(name) if handler and not is_valid_default_handler(handler): handler = None if not handler: # Use default_handler from project configuration. handler = self.default_handler if not is_valid_default_handler(handler): raise ConfigurationError( tag_("%(handler)s is not a valid default handler. Please " "update %(option)s through the %(page)s page or by " "directly editing trac.ini.", handler=tag.code(handler.__class__.__name__), option=tag.code("[trac] default_handler"), page=tag.a(_("Basic Settings"), href=req.href.admin('general/basics')))) return handler
def expand_macro(self, formatter, name, args): from trac.config import ConfigSection, Option section_filter = key_filter = '' args, kw = parse_args(args) if args: section_filter = args.pop(0).strip() if args: key_filter = args.pop(0).strip() registry = ConfigSection.get_registry(self.compmgr) sections = dict((name, dgettext(section.doc_domain, to_unicode(section.__doc__))) for name, section in registry.iteritems() if name.startswith(section_filter)) registry = Option.get_registry(self.compmgr) options = {} for (section, key), option in registry.iteritems(): if section.startswith(section_filter): options.setdefault(section, {})[key] = option sections.setdefault(section, '') return tag.div(class_='tracini')( (tag.h3(tag.code('[%s]' % section), id='%s-section' % section), format_to_html(self.env, formatter.context, section_doc), tag.table(class_='wiki')(tag.tbody( tag.tr(tag.td(tag.tt(option.name)), tag.td(format_to_oneliner( self.env, formatter.context, dgettext(option.doc_domain, to_unicode(option.__doc__)))), tag.td(tag.code(option.default or 'false') if option.default or option.default is False else _("(no default)"), class_='default' if option.default or option.default is False else 'nodefault')) for option in sorted(options.get(section, {}).itervalues(), key=lambda o: o.name) if option.name.startswith(key_filter)))) for section, section_doc in sorted(sections.iteritems()))
def _get_valid_default_handler(self, req): # Use default_handler from the Session if it is a valid value. name = req.session.get('default_handler') handler = self._request_handlers.get(name) if handler and not is_valid_default_handler(handler): handler = None if not handler: # Use default_handler from project configuration. handler = self.default_handler if not is_valid_default_handler(handler): raise ConfigurationError( tag_( "%(handler)s is not a valid default handler. Please " "update %(option)s through the %(page)s page or by " "directly editing trac.ini.", handler=tag.code(handler.__class__.__name__), option=tag.code("[trac] default_handler"), page=tag.a(_("Basic Settings"), href=req.href.admin('general/basics')))) return handler
def makeStream(self, node, indent=-2): if node.type == simpletree.Element.type: indent+=2 if node.type not in (simpletree.Document.type, simpletree.DocumentFragment.type): rv = self.serializeNode(node, indent) else: rv = tag() for child in node.childNodes: rv.append(self.makeStream(child, indent)) if node.type == simpletree.Element.type: rv.append(tag.code("</" + node.name + ">", class_=tagClasses["element"])) return rv
def __init__(self, path, log=None, params={}): self.cnx = None if path != ':memory:': if not os.access(path, os.F_OK): raise ConfigurationError(_('Database "%(path)s" not found.', path=path)) dbdir = os.path.dirname(path) if not os.access(path, os.R_OK + os.W_OK) or \ not os.access(dbdir, os.R_OK + os.W_OK): raise ConfigurationError(tag_( "The user %(user)s requires read _and_ write permissions " "to the database file %(path)s and the directory it is " "located in.", user=tag.code(getuser()), path=tag.code(path))) self._active_cursors = weakref.WeakKeyDictionary() timeout = int(params.get('timeout', 10.0)) self._eager = params.get('cursor', 'eager') == 'eager' # eager is default, can be turned off by specifying ?cursor= if isinstance(path, unicode): # needed with 2.4.0 path = path.encode('utf-8') cnx = sqlite.connect(path, detect_types=sqlite.PARSE_DECLTYPES, check_same_thread=sqlite_version < (3, 3, 1), timeout=timeout) # load extensions extensions = params.get('extensions', []) if len(extensions) > 0: cnx.enable_load_extension(True) for ext in extensions: cnx.load_extension(ext) cnx.enable_load_extension(False) cursor = cnx.cursor() _set_journal_mode(cursor, params.get('journal_mode')) _set_synchronous(cursor, params.get('synchronous')) ConnectionWrapper.__init__(self, cnx, log)
def expand_macro(self, formatter, name, content): from trac.config import ConfigSection, Option section_filter = key_filter = '' args, kw = parse_args(content) if args: section_filter = args.pop(0).strip() if args: key_filter = args.pop(0).strip() def getdoc(option_or_section): doc = to_unicode(option_or_section.__doc__) if doc: doc = dgettext(option_or_section.doc_domain, doc) return doc registry = ConfigSection.get_registry(self.compmgr) sections = dict((name, getdoc(section)) for name, section in registry.iteritems() if name.startswith(section_filter)) registry = Option.get_registry(self.compmgr) options = {} for (section, key), option in registry.iteritems(): if section.startswith(section_filter): options.setdefault(section, {})[key] = option sections.setdefault(section, '') def default_cell(option): default = option.default if default is not None and default != '': return tag.td(tag.code(option.dumps(default)), class_='default') else: return tag.td(_("(no default)"), class_='nodefault') return tag.div(class_='tracini')( (tag.h3(tag.code('[%s]' % section), id='%s-section' % section), format_to_html(self.env, formatter.context, section_doc), tag.table(class_='wiki')(tag.tbody( tag.tr(tag.td(tag.tt(option.name)), tag.td(format_to_oneliner( self.env, formatter.context, getdoc(option))), default_cell(option), class_='odd' if idx % 2 else 'even') for idx, option in enumerate(sorted(options.get(section, {}).itervalues(), key=lambda o: o.name)) if option.name.startswith(key_filter)))) for section, section_doc in sorted(sections.iteritems()))
def options_table(section, options): if options: return tag.table(class_='wiki')( tag.tbody( tag.tr( tag.td(tag.a(tag.code(option.name), class_='tracini-option', href='#%s-%s-option' % (section, option.name))), tag.td(format_to_html(self.env, formatter.context, option.doc)), default_cell(option), id='%s-%s-option' % (section, option.name), class_='odd' if idx % 2 else 'even') for idx, option in enumerate(options)))
def expand_macro(self, formatter, name, content): largs, kwargs = parse_args(content) largs.append('') wlink = largs[0] raw = True if 'raw' in kwargs and kwargs['raw'].lower() == 'false': raw = False url = extract_url (self.env, formatter.context, wlink, raw) return tag.p( tag.code ("'%s'" % wlink), tag.span (' => '), tag.a ("'%s'" % url, href=url), class_='extracturl', )
def default_cell(option): default = option.default if default is True: default = 'true' elif default is False: default = 'false' elif default == 0: default = '0.0' if isinstance(default, float) else '0' elif default: default = ', '.join(to_unicode(val) for val in default) \ if isinstance(default, (list, tuple)) \ else to_unicode(default) else: return tag.td(_("(no default)"), class_='nodefault') return tag.td(tag.code(default), class_='default')
def expand_macro(self, formatter, name, content): content = content.strip() if content else '' name_filter = content.strip('*') items = {} for subscriber in NotificationSystem(self.env).subscribers: name = subscriber.__class__.__name__ if not name_filter or name.startswith(name_filter): items[name] = subscriber.description() return tag.div(class_='trac-subscriberlist')(tag.table(class_='wiki')( tag.thead(tag.tr(tag.th(_("Subscriber")), tag.th(_("Description")))), tag.tbody( tag.tr(tag.td(tag.code(name)), tag.td(items[name]), class_='odd' if idx % 2 else 'even') for idx, name in enumerate(sorted(items.keys())))))
def expand_macro(self, formatter, name, content): content = content.strip() if content else '' name_filter = content.strip('*') items = {} for subscriber in NotificationSystem(self.env).subscribers: name = subscriber.__class__.__name__ if not name_filter or name.startswith(name_filter): items[name] = subscriber.description() return tag.div(class_='trac-subscriberlist')( tag.table(class_='wiki')( tag.thead(tag.tr( tag.th(_("Subscriber")), tag.th(_("Description")))), tag.tbody( tag.tr(tag.td(tag.code(name)), tag.td(items[name]), class_='odd' if idx % 2 else 'even') for idx, name in enumerate(sorted(items.keys())))))
def send(self, from_addr, recipients, message): # Use native line endings in message message = fix_eol(message, os.linesep) self.log.info("Sending notification through sendmail at %s to %s", self.sendmail_path, recipients) cmdline = [self.sendmail_path, '-i', '-f', from_addr] + recipients self.log.debug("Sendmail command line: %s", cmdline) try: child = Popen(cmdline, bufsize=-1, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=close_fds) except OSError as e: raise ConfigurationError( tag_("Sendmail error (%(error)s). Please modify %(option)s " "in your configuration.", error=to_unicode(e), option=tag.code("[notification] sendmail_path"))) out, err = child.communicate(message) if child.returncode or err: raise Exception("Sendmail failed with (%s, %s), command: '%s'" % (child.returncode, err.strip(), cmdline))
def parse_connection_uri(db_str): """Parse the database connection string. The database connection string for an environment is specified through the `database` option in the `[trac]` section of trac.ini. :return: a tuple containing the scheme and a dictionary of attributes: `user`, `password`, `host`, `port`, `path`, `params`. :since: 1.1.3 """ if not db_str: section = tag.a("[trac]", title=_("TracIni documentation"), class_='trac-target-new', href='http://trac.edgewall.org/wiki/TracIni' '#trac-section') raise ConfigurationError( tag_("Database connection string is empty. Set the %(option)s " "configuration option in the %(section)s section of " "trac.ini. Please refer to the %(doc)s for help.", option=tag.code("database"), section=section, doc=_doc_db_str())) try: scheme, rest = db_str.split(':', 1) except ValueError: raise _invalid_db_str(db_str) if not rest.startswith('/'): if scheme == 'sqlite' and rest: # Support for relative and in-memory SQLite connection strings host = None path = rest else: raise _invalid_db_str(db_str) else: if not rest.startswith('//'): host = None rest = rest[1:] elif rest.startswith('///'): host = None rest = rest[3:] else: rest = rest[2:] if '/' in rest: host, rest = rest.split('/', 1) else: host = rest rest = '' path = None if host and '@' in host: user, host = host.split('@', 1) if ':' in user: user, password = user.split(':', 1) else: password = None if user: user = urllib.unquote(user) if password: password = unicode_passwd(urllib.unquote(password)) else: user = password = None if host and ':' in host: host, port = host.split(':', 1) try: port = int(port) except ValueError: raise _invalid_db_str(db_str) else: port = None if not path: path = '/' + rest if os.name == 'nt': # Support local paths containing drive letters on Win32 if len(rest) > 1 and rest[1] == '|': path = "%s:%s" % (rest[0], rest[2:]) params = {} if '?' in path: path, qs = path.split('?', 1) qs = qs.split('&') for param in qs: try: name, value = param.split('=', 1) except ValueError: raise _invalid_db_str(db_str) value = urllib.unquote(value) params[name] = value args = zip(('user', 'password', 'host', 'port', 'path', 'params'), (user, password, host, port, path, params)) return scheme, dict([(key, value) for key, value in args if value])
def render_admin_panel(self, req, category, page, path_info): # Retrieve info for all repositories rm = RepositoryManager(self.env) all_repos = rm.get_all_repositories() db_provider = self.env[DbRepositoryProvider] if path_info: # Detail view reponame = path_info if not is_default(path_info) else "" info = all_repos.get(reponame) if info is None: raise TracError(_("Repository '%(repo)s' not found", repo=path_info)) if req.method == "POST": if req.args.get("cancel"): req.redirect(req.href.admin(category, page)) elif db_provider and req.args.get("save"): # Modify repository changes = {} valid = True for field in db_provider.repository_attrs: value = normalize_whitespace(req.args.get(field)) if (value is not None or field in ("hidden", "sync_per_request")) and value != info.get(field): changes[field] = value if "dir" in changes and not self._check_dir(req, changes["dir"]): valid = False if valid and changes: db_provider.modify_repository(reponame, changes) add_notice(req, _("Your changes have been saved.")) name = req.args.get("name") resync = tag.code( "trac-admin %s repository resync " '"%s"' % (self.env.path, name or "(default)") ) if "dir" in changes: msg = tag_( "You should now run %(resync)s to " "synchronize Trac with the repository.", resync=resync, ) add_notice(req, msg) elif "type" in changes: msg = tag_( "You may have to run %(resync)s to " "synchronize Trac with the repository.", resync=resync, ) add_notice(req, msg) if name and name != path_info and not "alias" in info: cset_added = tag.code( "trac-admin %s changeset " 'added "%s" $REV' % (self.env.path, name or "(default)") ) msg = tag_( "You will need to update your " "post-commit hook to call " "%(cset_added)s with the new " "repository name.", cset_added=cset_added, ) add_notice(req, msg) if valid: req.redirect(req.href.admin(category, page)) Chrome(self.env).add_wiki_toolbars(req) data = {"view": "detail", "reponame": reponame} else: # List view if req.method == "POST": # Add a repository if db_provider and req.args.get("add_repos"): name = req.args.get("name") type_ = req.args.get("type") # Avoid errors when copy/pasting paths dir = normalize_whitespace(req.args.get("dir", "")) if name is None or type_ is None or not dir: add_warning(req, _("Missing arguments to add a " "repository.")) elif self._check_dir(req, dir): try: db_provider.add_repository(name, dir, type_) except self.env.db_exc.IntegrityError: name = name or "(default)" raise TracError(_('The repository "%(name)s" ' "already exists.", name=name)) name = name or "(default)" add_notice(req, _('The repository "%(name)s" has been ' "added.", name=name)) resync = tag.code("trac-admin %s repository resync " '"%s"' % (self.env.path, name)) msg = tag_( "You should now run %(resync)s to " "synchronize Trac with the repository.", resync=resync ) add_notice(req, msg) cset_added = tag.code("trac-admin %s changeset " 'added "%s" $REV' % (self.env.path, name)) doc = tag.a(_("documentation"), href=req.href.wiki("TracRepositoryAdmin") + "#Synchronization") msg = tag_( "You should also set up a post-commit hook " "on the repository to call %(cset_added)s " "for each committed changeset. See the " "%(doc)s for more information.", cset_added=cset_added, doc=doc, ) add_notice(req, msg) req.redirect(req.href.admin(category, page)) # Add a repository alias elif db_provider and req.args.get("add_alias"): name = req.args.get("name") alias = req.args.get("alias") if name is not None and alias is not None: try: db_provider.add_alias(name, alias) except self.env.db_exc.IntegrityError: raise TracError(_('The alias "%(name)s" already ' "exists.", name=name or "(default)")) add_notice(req, _('The alias "%(name)s" has been ' "added.", name=name or "(default)")) req.redirect(req.href.admin(category, page)) add_warning(req, _("Missing arguments to add an " "alias.")) # Refresh the list of repositories elif req.args.get("refresh"): req.redirect(req.href.admin(category, page)) # Remove repositories elif db_provider and req.args.get("remove"): sel = req.args.getlist("sel") if sel: for name in sel: db_provider.remove_repository(name) add_notice(req, _("The selected repositories have " "been removed.")) req.redirect(req.href.admin(category, page)) add_warning(req, _("No repositories were selected.")) data = {"view": "list"} # Find repositories that are editable db_repos = {} if db_provider is not None: db_repos = dict(db_provider.get_repositories()) # Prepare common rendering data repositories = dict( (reponame, self._extend_info(reponame, info.copy(), reponame in db_repos)) for (reponame, info) in all_repos.iteritems() ) types = sorted([""] + rm.get_supported_types()) data.update({"types": types, "default_type": rm.default_repository_type, "repositories": repositories}) return "admin_repositories.html", data
def alert_disabled(self): return tag.div(tag.span('Error', class_='label label-important'), ' Could not load dashboard. Is ', tag.code('bhdashboard.web_ui.DashboardModule'), ' component disabled ?', class_='alert alert-error')
def expand_macro(self, formatter, name, content): from trac.config import ConfigSection, Option args, kw = parse_args(content) filters = {} for name, index in (('section', 0), ('option', 1)): pattern = kw.get(name, '').strip() if pattern: filters[name] = fnmatch.translate(pattern) continue prefix = args[index].strip() if index < len(args) else '' if prefix: filters[name] = re.escape(prefix) has_option_filter = 'option' in filters for name in ('section', 'option'): filters[name] = re.compile(filters[name], re.IGNORECASE).match \ if name in filters \ else lambda v: True section_filter = filters['section'] option_filter = filters['option'] section_registry = ConfigSection.get_registry(self.compmgr) option_registry = Option.get_registry(self.compmgr) options = {} for (section, key), option in option_registry.iteritems(): if section_filter(section) and option_filter(key): options.setdefault(section, {})[key] = option if not has_option_filter: for section in section_registry: if section_filter(section): options.setdefault(section, {}) for section in options: options[section] = sorted(options[section].itervalues(), key=lambda option: option.name) sections = [(section, section_registry[section].doc if section in section_registry else '') for section in sorted(options)] def default_cell(option): default = option.default if default is not None and default != '': return tag.td(tag.code(option.dumps(default)), class_='default') else: return tag.td(_("(no default)"), class_='nodefault') def options_table(section, options): if options: return tag.table(class_='wiki')( tag.tbody( tag.tr( tag.td(tag.a(tag.code(option.name), class_='tracini-option', href='#%s-%s-option' % (section, option.name))), tag.td(format_to_html(self.env, formatter.context, option.doc)), default_cell(option), id='%s-%s-option' % (section, option.name), class_='odd' if idx % 2 else 'even') for idx, option in enumerate(options))) return tag.div(class_='tracini')( (tag.h3(tag.code('[%s]' % section), id='%s-section' % section), format_to_html(self.env, formatter.context, section_doc), options_table(section, options.get(section))) for section, section_doc in sections)
def _invalid_db_str(db_str): return ConfigurationError( tag_("Invalid format %(db_str)s for the database connection string. " "Please refer to the %(doc)s for help.", db_str=tag.code(db_str), doc=_doc_db_str()))