Пример #1
0
 def pretty_dateinfo(date, format=None, dateonly=False):
     if not date:
         return ''
     if format == 'date':
         absolute = user_time(req, format_date, date)
     else:
         absolute = user_time(req, format_datetime, date)
     now = datetime_now(localtz)
     relative = pretty_timedelta(date, now)
     if not format:
         format = req.session.get(
             'dateinfo',
             Chrome(self.env).default_dateinfo_format)
     if format == 'relative':
         if date > now:
             label = _("in %(relative)s", relative=relative) \
                     if not dateonly else relative
             title = _("on %(date)s at %(time)s",
                       date=user_time(req, format_date, date),
                       time=user_time(req, format_time, date))
             return tag.span(label, title=title)
         else:
             label = _("%(relative)s ago", relative=relative) \
                     if not dateonly else relative
             title = _("See timeline at %(absolutetime)s",
                       absolutetime=absolute)
     else:
         if dateonly:
             label = absolute
         elif req.lc_time == 'iso8601':
             label = _("at %(iso8601)s", iso8601=absolute)
         elif format == 'date':
             label = _("on %(date)s", date=absolute)
         else:
             label = _("on %(date)s at %(time)s",
                       date=user_time(req, format_date, date),
                       time=user_time(req, format_time, date))
         if date > now:
             title = _("in %(relative)s", relative=relative)
             return tag.span(label, title=title)
         title = _("See timeline %(relativetime)s ago",
                   relativetime=relative)
     return self.get_timeline_link(req,
                                   date,
                                   label,
                                   precision='second',
                                   title=title)
Пример #2
0
    def filter_stream(self, req, method, filename, stream, data):
        if 'ticket' in data:
            latest = max([0] + [
                int(c[3]) for c in data['ticket'].get_changelog()
                if c[2] == u'comment' and c[3] != u''
            ])
            ticket = data['ticket'].resource
            if ticket.version == None:  # showing latest version, add fix-version link
                add_ctxtnav(req, _('Previous Version'), '?version=%s' % latest)
            else:  # showing specified version; add latest-version link
                r, i, v = ticket.realm, ticket.id, ticket.version
                prev_link, next_link = None, None
                if v > 0:
                    prev_link = tag.a(_('Previous Version'),
                                      href=get_resource_url(
                                          self.env, Resource(r, i, v - 1),
                                          req.href),
                                      class_='prev')
                if v < latest:
                    next_link = tag.a(_('Next Version'),
                                      href=get_resource_url(
                                          self.env, Resource(r, i, v + 1),
                                          req.href),
                                      class_='next')

                add_ctxtnav(
                    req,
                    tag.span(Markup('&larr; '),
                             prev_link or _('Previous Version'),
                             class_=not prev_link and 'missing' or None))
                add_ctxtnav(
                    req,
                    tag.a(_('View Latest Version'),
                          href=get_resource_url(self.env, Resource(r, i, None),
                                                req.href)))
                add_ctxtnav(
                    req,
                    tag.span(next_link or _('Next Version'),
                             Markup(' &rarr;'),
                             class_=not next_link and 'missing' or None))
        return stream
Пример #3
0
def _get_revs_link(label, context, spath, revs, title=None):
    """Return a link to the revision log when more than one revision is
    given, to the revision itself for a single revision, or a `<span>`
    with "no revision" for none.
    """
    reponame = context.resource.parent.id
    if not revs:
        return tag.span(label, title=_('No revisions'))
    elif ',' in revs or '-' in revs:
        revs_href = context.href.log(reponame or None, spath, revs=revs)
    else:
        revs_href = context.href.changeset(revs, reponame or None, spath)
    revs = revs.replace(',', ', ')
    if title:
        title = _("%(title)s: %(revs)s", title=title, revs=revs)
    else:
        title = revs
    return tag.a(label, title=title, href=revs_href)
Пример #4
0
    def _format_tagged(self, formatter, ns, target, label, fullmatch=None):
        """Tag and tag query expression link formatter."""
        def unquote(text):
            """Strip all matching pairs of outer quotes from string."""
            while re.match(WikiParser.QUOTED_STRING, text):
                # Remove outer whitespace after stripped quotation too.
                text = text[1:-1].strip()
            return text

        label = label and unquote(label.strip()) or ''
        target = unquote(target.strip())

        query = target
        # Pop realms from query expression.
        all_realms = self.tag_system.get_taggable_realms(formatter.perm)
        realms = query_realms(target, all_realms)
        if realms:
            kwargs = dict((realm, 'on') for realm in realms)
            target = re.sub('(^|\W)realm:\S+(\W|$)', ' ', target).strip()
        else:
            kwargs = {}

        tag_res = Resource('tag', target)
        if 'TAGS_VIEW' not in formatter.perm(tag_res):
            return tag.span(label,
                            class_='forbidden tags',
                            title=_("no permission to view tags"))

        context = formatter.context
        href = self.tag_system.get_resource_url(tag_res, context.href, kwargs)
        if all_realms and (target in self.tag_system.get_all_tags(
                formatter.req) or not iter_is_empty(
                    self.tag_system.query(formatter.req, query))):
            # At least one tag provider is available and tag exists or
            # tags query yields at least one match.
            if label:
                return tag.a(label, href=href)
            return render_resource_link(self.env, context, tag_res)

        return tag.a(label + '?',
                     href=href,
                     class_='missing tags',
                     rel='nofollow')
Пример #5
0
    def expand_macro(self, formatter, name, content, args=None):
        style_args = {
            'fg': 'color',
            'bg': 'background-color',
            'size': 'font-size'
        }
        style_values = {'color': '', 'background-color': '', 'font-size': ''}
        space_start = ''
        space_end = ''
        if args:
            text = content
            for k in args.keys():
                style = style_args[k] if k in style_args else k
                style_values[style] = args.get(k)
            html = format_to_html(self.env, formatter.context, text)
        else:
            args = content.split(',')
            text = ','.join(args[:-1])
            args = args[-1].split('/') + [''] * 3
            style_values['color'] = args.pop(0).strip()
            # background color is optional
            arg = args.pop(0).strip()
            if len(arg) > 0 and arg[0].isdigit():
                style_values['font-size'] = arg
            else:
                style_values['background-color'] = arg
                style_values['font-size'] = args.pop(0).strip()
            html = format_to_oneliner(self.env, formatter.context, text)
            if text.startswith(u' '):
                space_start = Markup('&nbsp')
            if text.endswith(u' '):
                space_end = Markup('&nbsp')
        if style_values['font-size'].isdigit():
            style_values['font-size'] = '%s%%' % style_values['font-size']
        style = ';'.join('%s:%s' % (k, v)
                         for (k, v) in style_values.iteritems() if v)

        span = sanitize_attrib(self.env, tag.span(style=style))
        span(space_start, html, space_end)
        return span
Пример #6
0
    def process_request(self, req):
        req.perm.require('LOG_VIEW')

        mode = req.args.get('mode', 'stop_on_copy')
        path = req.args.get('path', '/')
        rev = req.args.get('rev')
        stop_rev = req.args.get('stop_rev')
        revs = req.args.get('revs')
        format = req.args.get('format')
        verbose = req.args.get('verbose')
        limit = req.args.getint('limit', self.default_log_limit)

        rm = RepositoryManager(self.env)
        reponame, repos, path = rm.get_repository_by_path(path)

        if not repos:
            if path == '/':
                raise TracError(
                    _("No repository specified and no default"
                      " repository configured."))
            else:
                raise ResourceNotFound(
                    _("Repository '%(repo)s' not found",
                      repo=reponame or path.strip('/')))

        if reponame != repos.reponame:  # Redirect alias
            qs = req.query_string
            req.redirect(
                req.href.log(repos.reponame or None, path) +
                ('?' + qs if qs else ''))

        normpath = repos.normalize_path(path)

        # if `revs` parameter is given, then we're restricted to the
        # corresponding revision ranges.
        # If not, then we're considering all revisions since `rev`,
        # on that path, in which case `revranges` will be None.
        if revs:
            revranges = RevRanges(repos, revs, resolve=True)
            rev = revranges.b
        else:
            revranges = None
            rev = repos.normalize_rev(rev)

        # The `history()` method depends on the mode:
        #  * for ''stop on copy'' and ''follow copies'', it's `Node.history()`
        #    unless explicit ranges have been specified
        #  * for ''show only add, delete'' we're using
        #   `Repository.get_path_history()`
        cset_resource = repos.resource.child(self.realm)
        show_graph = False
        curr_revrange = []
        if mode == 'path_history':

            def history():
                for h in repos.get_path_history(path, rev):
                    if 'CHANGESET_VIEW' in req.perm(cset_resource(id=h[1])):
                        yield h

        elif revranges:
            show_graph = path == '/' and not verbose \
                         and not repos.has_linear_changesets \
                         and len(revranges) == 1

            def history():
                separator = False
                for a, b in reversed(revranges.pairs):
                    curr_revrange[:] = (a, b)
                    node = get_existing_node(req, repos, path, b)
                    for p, rev, chg in node.get_history():
                        if repos.rev_older_than(rev, a):
                            break
                        if 'CHANGESET_VIEW' in req.perm(cset_resource(id=rev)):
                            separator = True
                            yield p, rev, chg
                    else:
                        separator = False
                    if separator:
                        yield p, rev, None
        else:
            show_graph = path == '/' and not verbose \
                         and not repos.has_linear_changesets

            def history():
                node = get_existing_node(req, repos, path, rev)
                for h in node.get_history():
                    if 'CHANGESET_VIEW' in req.perm(cset_resource(id=h[1])):
                        yield h

        # -- retrieve history, asking for limit+1 results
        info = []
        depth = 1
        previous_path = normpath
        count = 0
        history_remaining = True
        for old_path, old_rev, old_chg in history():
            if stop_rev and repos.rev_older_than(old_rev, stop_rev):
                break
            old_path = repos.normalize_path(old_path)

            item = {
                'path': old_path,
                'rev': old_rev,
                'existing_rev': old_rev,
                'change': old_chg,
                'depth': depth,
            }

            if old_chg == Changeset.DELETE:
                item['existing_rev'] = repos.previous_rev(old_rev, old_path)
            if not (mode == 'path_history' and old_chg == Changeset.EDIT):
                info.append(item)
            if old_path and old_path != previous_path and \
                    not (mode == 'path_history' and old_path == normpath):
                depth += 1
                item['depth'] = depth
                item['copyfrom_path'] = old_path
                if mode == 'stop_on_copy':
                    break
                elif mode == 'path_history':
                    depth -= 1
            if old_chg is None:  # separator entry
                stop_limit = limit
            else:
                count += 1
                stop_limit = limit + 1
            if count >= stop_limit:
                break
            previous_path = old_path
        else:
            history_remaining = False
        if not info:
            node = get_existing_node(req, repos, path, rev)
            if repos.rev_older_than(stop_rev, node.created_rev):
                # FIXME: we should send a 404 error here
                raise TracError(
                    _(
                        "The file or directory '%(path)s' doesn't "
                        "exist at revision %(rev)s or at any "
                        "previous revision.",
                        path=path,
                        rev=repos.display_rev(rev)), _('Nonexistent path'))

        # Generate graph data
        graph = {}
        if show_graph:
            threads, vertices, columns = \
                make_log_graph(repos, (item['rev'] for item in info))
            graph.update(threads=threads,
                         vertices=vertices,
                         columns=columns,
                         colors=self.graph_colors,
                         line_width=0.04,
                         dot_radius=0.1)
            add_script(req, 'common/js/excanvas.js', ie_if='IE')
            add_script(req, 'common/js/log_graph.js')
            add_script_data(req, graph=graph)

        def make_log_href(path, **args):
            link_rev = rev
            if rev == str(repos.youngest_rev):
                link_rev = None
            params = {'rev': link_rev, 'mode': mode, 'limit': limit}
            params.update(args)
            if verbose:
                params['verbose'] = verbose
            return req.href.log(repos.reponame or None, path, **params)

        if format in ('rss', 'changelog'):
            info = [i for i in info if i['change']]  # drop separators
            if info and count > limit:
                del info[-1]
        elif info and history_remaining and count >= limit:
            # stop_limit reached, there _might_ be some more
            next_rev = info[-1]['rev']
            next_path = info[-1]['path']
            next_revranges = None
            if curr_revrange:
                new_revrange = (curr_revrange[0], next_rev) \
                               if info[-1]['change'] else None
                next_revranges = revranges.truncate(curr_revrange,
                                                    new_revrange)
                next_revranges = unicode(next_revranges) or None
            if next_revranges or not revranges:
                older_revisions_href = make_log_href(next_path,
                                                     rev=next_rev,
                                                     revs=next_revranges)
                add_link(
                    req, 'next', older_revisions_href,
                    _('Revision Log (restarting at %(path)s, rev. '
                      '%(rev)s)',
                      path=next_path,
                      rev=repos.display_rev(next_rev)))
            # only show fully 'limit' results, use `change == None` as a marker
            info[-1]['change'] = None

        revisions = [i['rev'] for i in info]
        changes = get_changes(repos, revisions, self.log)
        extra_changes = {}

        if format == 'changelog':
            for rev in revisions:
                changeset = changes[rev]
                cs = {}
                cs['message'] = wrap(changeset.message,
                                     70,
                                     initial_indent='\t',
                                     subsequent_indent='\t')
                files = []
                actions = []
                for cpath, kind, chg, bpath, brev in changeset.get_changes():
                    files.append(bpath if chg == Changeset.DELETE else cpath)
                    actions.append(chg)
                cs['files'] = files
                cs['actions'] = actions
                extra_changes[rev] = cs

        data = {
            'context':
            web_context(req, 'source', path, parent=repos.resource),
            'reponame':
            repos.reponame or None,
            'repos':
            repos,
            'path':
            path,
            'rev':
            rev,
            'stop_rev':
            stop_rev,
            'display_rev':
            repos.display_rev,
            'revranges':
            revranges,
            'mode':
            mode,
            'verbose':
            verbose,
            'limit':
            limit,
            'items':
            info,
            'changes':
            changes,
            'extra_changes':
            extra_changes,
            'graph':
            graph,
            'wiki_format_messages':
            self.config['changeset'].getbool('wiki_format_messages')
        }

        if format == 'changelog':
            return 'revisionlog.txt', data, {'content_type': 'text/plain'}
        elif format == 'rss':
            data['context'] = web_context(req,
                                          'source',
                                          path,
                                          parent=repos.resource,
                                          absurls=True)
            return ('revisionlog.rss', data, {
                'content_type': 'application/rss+xml'
            })

        item_ranges = []
        range = []
        for item in info:
            if item['change'] is None:  # separator
                if range:  # start new range
                    range.append(item)
                    item_ranges.append(range)
                    range = []
            else:
                range.append(item)
        if range:
            item_ranges.append(range)
        data['item_ranges'] = item_ranges

        add_stylesheet(req, 'common/css/diff.css')
        add_stylesheet(req, 'common/css/browser.css')

        path_links = get_path_links(req.href, repos.reponame, path, rev)
        if path_links:
            data['path_links'] = path_links
        if path != '/':
            add_link(req, 'up', path_links[-2]['href'], _('Parent directory'))

        rss_href = make_log_href(path,
                                 format='rss',
                                 revs=revs,
                                 stop_rev=stop_rev)
        add_link(req, 'alternate', auth_link(req, rss_href), _('RSS Feed'),
                 'application/rss+xml', 'rss')
        changelog_href = make_log_href(path,
                                       format='changelog',
                                       revs=revs,
                                       stop_rev=stop_rev)
        add_link(req, 'alternate', changelog_href, _('ChangeLog'),
                 'text/plain')

        add_ctxtnav(req,
                    _('View Latest Revision'),
                    href=req.href.browser(repos.reponame or None, path))
        if 'next' in req.chrome['links']:
            next = req.chrome['links']['next'][0]
            add_ctxtnav(
                req,
                tag.span(tag.a(_('Older Revisions'), href=next['href']),
                         Markup(' &rarr;')))

        return 'revisionlog.html', data
Пример #7
0
    def render_property(self, name, mode, context, props):

        def sha_link(sha, label=None):
            # sha is assumed to be a non-abbreviated 40-chars sha id
            try:
                reponame = context.resource.parent.id
                repos = RepositoryManager(self.env).get_repository(reponame)
                cset = repos.get_changeset(sha)
                if label is None:
                    label = repos.display_rev(sha)

                return tag.a(label, class_='changeset',
                             title=shorten_line(cset.message),
                             href=context.href.changeset(sha, repos.reponame))

            except Exception as e:
                return tag.a(sha, class_='missing changeset',
                             title=to_unicode(e), rel='nofollow')

        if name == 'Branches':
            branches = props[name]

            # simple non-merge commit
            return tag(*intersperse(', ', (sha_link(rev, label)
                                           for label, rev in branches)))

        elif name in ('Parents', 'Children'):
            revs = props[name] # list of commit ids

            if name == 'Parents' and len(revs) > 1:
                # we got a merge...
                current_sha = context.resource.id
                reponame = context.resource.parent.id

                parent_links = intersperse(', ',
                                           ((sha_link(rev),
                      ' (',
                      tag.a(_("diff"),
                            title=_("Diff against this parent (show the "
                                    "changes merged from the other parents)"),
                            href=context.href.changeset(current_sha, reponame,
                                                        old=rev)),
                      ')')
                     for rev in revs))

                return tag(list(parent_links),
                           tag.br(),
                           tag.span(Markup(_("Note: this is a <strong>merge"
                                             "</strong> changeset, the "
                                             "changes displayed below "
                                             "correspond to the merge "
                                             "itself.")),
                                    class_='hint'),
                           tag.br(),
                           tag.span(Markup(_("Use the <code>(diff)</code> "
                                             "links above to see all the "
                                             "changes relative to each "
                                             "parent.")),
                                    class_='hint'))

            # simple non-merge commit
            return tag(*intersperse(', ', map(sha_link, revs)))

        elif name in ('git-committer', 'git-author'):
            user_, time_ = props[name]
            _str = "%s (%s)" % (
                Chrome(self.env).format_author(context.req, user_),
                format_datetime(time_, tzinfo=context.req.tz))
            return unicode(_str)

        raise TracError(_("Internal error"))