def make_list(items): parts = [item.rsplit('.', 1) for item in items] return tag.table(tag.tbody( tag.tr(tag.td(c, class_='trac-name'), tag.td('(%s.*)' % m, class_='trac-name')) for m, c in parts), class_='trac-pluglist')
def expand_macro(self, formatter, name, content): intertracs = {} for key, value in self.intertrac_section.options(): idx = key.rfind('.') if idx > 0: # 0 itself doesn't help much: .xxx = ... prefix, attribute = key[:idx], key[idx+1:] intertrac = intertracs.setdefault(prefix, {}) intertrac[attribute] = value else: intertracs[key] = value # alias if 'trac' not in intertracs: intertracs['trac'] = {'title': _('The Trac Project'), 'url': 'http://trac.edgewall.org'} 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))) return tag.table(class_="wiki intertrac")( tag.tr(tag.th(tag.em(_("Prefix"))), tag.th(tag.em(_("Trac Site")))), [generate_prefix(p) for p in sorted(intertracs)])
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))))
def _render_mergeinfo(self, name, mode, context, props): rows = [] for row in props[name].splitlines(): try: (path, revs) = row.rsplit(':', 1) rows.append([tag.td(path), tag.td(revs.replace(',', u',\u200b'))]) except ValueError: rows.append(tag.td(row, colspan=2)) return tag.table(tag.tbody([tag.tr(row) for row in rows]), class_='props')
def expand_macro(self, formatter, name, content, args=None): t = [ render_table(p, '1', lambda s: self._format_phrase(formatter, s, None)) for p in [self.fixme_phrases, self.todo_phrases, self.done_phrases] ] style = 'border:none;text-align:center;vertical-align:top' spacer = tag.td(style='width:2em;border:none') return tag.table( tag.tr(tag.td(t[0], style=style), spacer, tag.td(t[1], style=style), spacer, tag.td(t[2], style=style)))
def _render_source(self, context, lines, 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(lines, unicode): lines = lines.splitlines(True) # elif isinstance(lines, list): # pass # assume these are lines already 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(lines): 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 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): interwikis = [] for k in sorted(self.keys()): prefix, url, title = self[k] interwikis.append({ 'prefix': prefix, 'url': url, 'title': title, 'rc_url': self._expand_or_append(url, ['RecentChanges']), 'description': url if title == prefix else title}) return tag.table(tag.tr(tag.th(tag.em(_("Prefix"))), tag.th(tag.em(_("Site")))), [tag.tr(tag.td(tag.a(w['prefix'], href=w['rc_url'])), tag.td(tag.a(w['description'], href=w['url']))) for w in interwikis ], class_="wiki interwiki")
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)))))
def render_table(items, colspec, render_item, colspace=1): try: columns = max(int(colspec), 1) except Exception: columns = 3 nbsp = Markup(' ') # hdr = [tag.th(nbsp, 'Display'), tag.th('Markup', nbsp)] spacer_style = 'width:%dem;border:none' % colspace # Find max width to make value cols equally wide width = 0 for i in items: if (isinstance(i, str) or isinstance(i, unicode)) and len(i) > width: width = len(i) value_style = 'border:none' #noinspection PyUnusedLocal if width: # empirical... value_style = '%s;width:%dem' % (value_style, width * 2 / 3) def render_def(s): rendered = s and render_item(s) or None if isinstance(s, str): s = Markup(s.replace('&', '&')) return [ tag.td(rendered, nbsp, style='border:none'), tag.td(tag.kbd(s), style=value_style) ] return tag.table( #tag.tr((hdr + [tag.td(style=spacer_style)]) * # (columns-1) + hdr), [ tag.tr([ render_def(s) + [tag.td(style=spacer_style)] for s in row[:-1] ] + render_def(row[-1])) for row in group_over(sorted(items), columns) ], class_='wiki', style='border:none')
def render_property_diff(self, name, old_context, old_props, new_context, new_props, options): # Build 5 columns table showing modifications on merge sources # || source || added || removed || added (ni) || removed (ni) || # || source || removed || rm = RepositoryManager(self.env) repos = rm.get_repository(old_context.resource.parent.id) def parse_sources(props): sources = {} value = props[name] lines = value.splitlines() if name == 'svn:mergeinfo' \ else value.split() for line in lines: path, revs = line.split(':', 1) spath = _path_within_scope(repos.scope, path) if spath is not None: inheritable, non_inheritable = _partition_inheritable(revs) sources[spath] = (set(Ranges(inheritable)), set(Ranges(non_inheritable))) return sources old_sources = parse_sources(old_props) new_sources = parse_sources(new_props) # Go through new sources, detect modified ones or added ones blocked = name.endswith('blocked') added_label = [_("merged: "), _("blocked: ")][blocked] removed_label = [_("reverse-merged: "), _("un-blocked: ")][blocked] added_ni_label = _("marked as non-inheritable: ") removed_ni_label = _("unmarked as non-inheritable: ") sources = [] changed_revs = {} changed_nodes = [] for spath, (new_revs, new_revs_ni) in new_sources.iteritems(): new_spath = spath not in old_sources if new_spath: old_revs = old_revs_ni = set() else: old_revs, old_revs_ni = old_sources.pop(spath) added = new_revs - old_revs removed = old_revs - new_revs # unless new revisions differ from old revisions if not added and not removed: continue added_ni = new_revs_ni - old_revs_ni removed_ni = old_revs_ni - new_revs_ni revs = sorted(added | removed | added_ni | removed_ni) try: node = repos.get_node(spath, revs[-1]) changed_nodes.append((node, revs[0])) except NoSuchNode: pass sources.append( (spath, new_spath, added, removed, added_ni, removed_ni)) if changed_nodes: changed_revs = repos._get_changed_revs(changed_nodes) def revs_link(revs, context): if revs: revs = to_ranges(revs) return _get_revs_link(revs.replace(',', u',\u200b'), context, spath, revs) modified_sources = [] for spath, new_spath, added, removed, added_ni, removed_ni in sources: if spath in changed_revs: revs = set(changed_revs[spath]) added &= revs removed &= revs added_ni &= revs removed_ni &= revs if added or removed: if new_spath: status = _(" (added)") else: status = None modified_sources.append( (spath, [_get_source_link(spath, new_context), status], added and tag(added_label, revs_link(added, new_context)), removed and tag(removed_label, revs_link(removed, old_context)), added_ni and tag(added_ni_label, revs_link(added_ni, new_context)), removed_ni and tag(removed_ni_label, revs_link(removed_ni, old_context)))) # Go through remaining old sources, those were deleted removed_sources = [] for spath, old_revs in old_sources.iteritems(): removed_sources.append( (spath, _get_source_link(spath, old_context))) if modified_sources or removed_sources: modified_sources.sort() removed_sources.sort() changes = tag.table(tag.tbody([ tag.tr(tag.td(c) for c in cols[1:]) for cols in modified_sources ], [ tag.tr(tag.td(src), tag.td(_('removed'), colspan=4)) for spath, src in removed_sources ]), class_='props') else: changes = tag.em(_(' (with no actual effect on merging)')) return tag.li(tag_('Property %(prop)s changed', prop=tag.strong(name)), changes)
def render_property(self, name, mode, context, props): """Parse svn:mergeinfo and svnmerge-* properties, converting branch names to links and providing links to the revision log for merged and eligible revisions. """ has_eligible = name in ('svnmerge-integrated', 'svn:mergeinfo') revs_label = _('blocked') if name.endswith('blocked') else _('merged') revs_cols = 2 if has_eligible else None reponame = context.resource.parent.id target_path = context.resource.id repos = RepositoryManager(self.env).get_repository(reponame) target_rev = context.resource.version if has_eligible: node = repos.get_node(target_path, target_rev) branch_starts = {} for path, rev in node.get_copy_ancestry(): if path not in branch_starts: branch_starts[path] = rev + 1 rows = [] eligible_infos = [] if name.startswith('svnmerge-'): sources = props[name].split() else: sources = props[name].splitlines() for line in sources: path, revs = line.split(':', 1) spath = _path_within_scope(repos.scope, path) if spath is None: continue revs = revs.strip() inheritable, non_inheritable = _partition_inheritable(revs) revs = ','.join(inheritable) deleted = False try: node = repos.get_node(spath, target_rev) resource = context.resource.parent.child('source', spath) if 'LOG_VIEW' in context.perm(resource): row = [ _get_source_link(spath, context), _get_revs_link(revs_label, context, spath, revs) ] if non_inheritable: non_inheritable = ','.join(non_inheritable) row.append( _get_revs_link( _('non-inheritable'), context, spath, non_inheritable, _('merged on the directory ' 'itself but not below'))) if has_eligible: first_rev = branch_starts.get(spath) if not first_rev: first_rev = node.get_branch_origin() eligible = set(xrange(first_rev or 1, target_rev + 1)) eligible -= set(Ranges(revs)) blocked = _get_blocked_revs(props, name, spath) if blocked: eligible -= set(Ranges(blocked)) if eligible: node = repos.get_node(spath, max(eligible)) eligible_infos.append((spath, node, eligible, row)) continue eligible = to_ranges(eligible) row.append( _get_revs_link(_('eligible'), context, spath, eligible)) rows.append((False, spath, [tag.td(each) for each in row])) continue except NoSuchNode: deleted = True revs = revs.replace(',', u',\u200b') rows.append( (deleted, spath, [tag.td('/' + spath), tag.td(revs, colspan=revs_cols)])) # fetch eligible revisions for each path at a time changed_revs = {} changed_nodes = [(node, min(eligible)) for spath, node, eligible, row in eligible_infos] if changed_nodes: changed_revs = repos._get_changed_revs(changed_nodes) for spath, node, eligible, row in eligible_infos: if spath in changed_revs: eligible &= set(changed_revs[spath]) else: eligible.clear() row.append( _get_revs_link(_("eligible"), context, spath, to_ranges(eligible))) rows.append((False, spath, [tag.td(each) for each in row])) if not rows: return None rows.sort() if rows and rows[-1][0]: toggledeleted = tag.a(_("(toggle deleted branches)"), class_='trac-toggledeleted', href='#') else: toggledeleted = None return tag( toggledeleted, tag.table(tag.tbody([ tag.tr(row, class_='trac-deleted' if deleted else None) for deleted, spath, row in rows ]), class_='props'))