예제 #1
0
    def _format_link(self, formatter, ns, match, label, fullmatch=None):
        if ns == 'log1':
            groups = fullmatch.groupdict()
            it_log = groups.get('it_log')
            revs = groups.get('log_revs')
            path = groups.get('log_path') or '/'
            target = '%s%s@%s' % (it_log, path, revs)
            # prepending it_log is needed, as the helper expects it there
            intertrac = formatter.shorthand_intertrac_helper(
                'log', target, label, fullmatch)
            if intertrac:
                return intertrac
            path, query, fragment = formatter.split_link(path)
        else:
            assert ns in ('log', 'log2')
            if ns == 'log':
                match, query, fragment = formatter.split_link(match)
            else:
                query = fragment = ''
                match = ''.join(reversed(match.split('/', 1)))
            path = match
            revs = ''
            if self.LOG_LINK_RE.match(match):
                indexes = [sep in match and match.index(sep) for sep in ':@']
                idx = min([i for i in indexes if i is not False])
                path, revs = match[:idx], match[idx+1:]

        rm = RepositoryManager(self.env)
        try:
            reponame, repos, path = rm.get_repository_by_path(path)
            if not reponame:
                reponame = rm.get_default_repository(formatter.context)
                if reponame is not None:
                    repos = rm.get_repository(reponame)

            if repos:
                if 'LOG_VIEW' in formatter.perm(repos.resource):
                    reponame = repos.reponame or None
                    path = path or '/'
                    revranges = RevRanges(repos, revs)
                    if revranges.has_ranges():
                        href = formatter.href.log(reponame, path,
                                                  revs=unicode(revranges))
                    else:
                        # try to resolve if single rev
                        repos.normalize_rev(revs)
                        href = formatter.href.log(reponame, path,
                                                  rev=revs or None)
                    if query and '?' in href:
                        query = '&' + query[1:]
                    return tag.a(label, class_='source',
                                 href=href + query + fragment)
                errmsg = _("No permission to view change log")
            elif reponame:
                errmsg = _("Repository '%(repo)s' not found", repo=reponame)
            else:
                errmsg = _("No default repository defined")
        except TracError as e:
            errmsg = to_unicode(e)
        return tag.a(label, class_='missing source', title=errmsg)
예제 #2
0
    def process_request(self, req):
        if req.method != 'POST':
            msg = u'Method not allowed (%s)\n' % req.method
            req.send(msg.encode('utf-8'), 'text/plain', 405)

        if req.args.get('token') != self.token:
            msg = u'Invalid token (%s)\n' % req.args.get('token')
            req.send(msg.encode('utf-8'), 'text/plain', 403)

        path = req.args.get('path', '/')
        rm = RepositoryManager(self.env)
        reponame, repos, path = rm.get_repository_by_path(path)

        output = u'Running hook on %s\n' % (reponame or '(default)')

        if self.autofetch:
            git = repos.git.repo
            output += u'* Running git fetch\n'
            output += git.fetch()
            output += u'* Updating references\n'
            remote_refs = git.for_each_ref(
                    "--format=%(refname)", "refs/remotes/origin").split()
            for remote_ref in remote_refs:
                local_ref = remote_ref.replace('remotes/origin', 'heads', 1)
                output += git.update_ref(local_ref, remote_ref)

        data = req.args.get('payload')
        if data:
            revs = [commit['id'] for commit in json.loads(data)['commits']]
            if revs:
                output += u'* Adding changesets %s\n' % u', '.join(revs)
                rm.notify('changeset_added', reponame, revs)

        req.send(output.encode('utf-8'), 'text/plain', 200 if output else 204)
예제 #3
0
 def _get_source(self, formatter, source_obj, dest_format):
     repos_mgr = RepositoryManager(self.env)
     try:  #0.12+
         repos_name, repos, source_obj = repos_mgr.get_repository_by_path(
             source_obj)
     except AttributeError, e:  #0.11
         repos = repos_mgr.get_repository(formatter.req.authname)
예제 #4
0
 def _read_source_from_repos(self, formatter, src_path):
     repos_mgr = RepositoryManager(self.env)
     try:  #0.12+
         repos_name, repos, source_obj = repos_mgr.get_repository_by_path(
             src_path)
     except AttributeError, e:  #0.11
         repos = repos_mgr.get_repository(formatter.req.authname)
    def _get_vcs_folders(self, req, q, dirname, prefix):
        rm = RepositoryManager(self.env)

        reponame, repos, path = rm.get_repository_by_path(dirname)
        repo_entries = {'text': _('Suggestions'),
                        'children': [],
                        }
        if repos:
            try:
                entries = ({'id': '/' + pathjoin(repos.reponame, e.path),
                           'text': '/' + pathjoin(repos.reponame, e.path),
                           'is_favorite': False
                            }
                           for e in repos.get_node(path).get_entries()
                           if e.can_view(req.perm)
                           and e.name.lower().startswith(prefix)
                           and e.isdir
                           )
                repo_entries['children'].extend(entries)

                if q.endswith('/'):
                    repo_entries['children'].append({'id': q,
                                                     'text': q,
                                                     'is_favorite': False
                                                     }
                                                    )
            except NoSuchNode:
                pass
        return repo_entries
예제 #6
0
파일: log.py 프로젝트: exocad/exotrac
    def _format_link(self, formatter, ns, match, label, fullmatch=None):
        if ns == 'log1':
            groups = fullmatch.groupdict()
            it_log = groups.get('it_log')
            revs = groups.get('log_revs')
            path = groups.get('log_path') or '/'
            target = '%s%s@%s' % (it_log, path, revs)
            # prepending it_log is needed, as the helper expects it there
            intertrac = formatter.shorthand_intertrac_helper(
                'log', target, label, fullmatch)
            if intertrac:
                return intertrac
            path, query, fragment = formatter.split_link(path)
        else:
            assert ns in ('log', 'log2')
            if ns == 'log':
                match, query, fragment = formatter.split_link(match)
            else:
                query = fragment = ''
                match = ''.join(reversed(match.split('/', 1)))
            path = match
            revs = ''
            if self.LOG_LINK_RE.match(match):
                indexes = [sep in match and match.index(sep) for sep in ':@']
                idx = min([i for i in indexes if i is not False])
                path, revs = match[:idx], match[idx+1:]

        rm = RepositoryManager(self.env)
        try:
            reponame, repos, path = rm.get_repository_by_path(path)
            if not reponame:
                reponame = rm.get_default_repository(formatter.context)
                if reponame is not None:
                    repos = rm.get_repository(reponame)

            if repos:
                if 'LOG_VIEW' in formatter.perm:
                    reponame = repos.reponame or None
                    path = path or '/'
                    revranges = RevRanges(repos, revs)
                    if revranges.has_ranges():
                        href = formatter.href.log(reponame, path,
                                                  revs=unicode(revranges))
                    else:
                        # try to resolve if single rev
                        repos.normalize_rev(revs)
                        href = formatter.href.log(reponame, path,
                                                  rev=revs or None)
                    if query and '?' in href:
                        query = '&' + query[1:]
                    return tag.a(label, class_='source',
                                 href=href + query + fragment)
                errmsg = _("No permission to view change log")
            elif reponame:
                errmsg = _("Repository '%(repo)s' not found", repo=reponame)
            else:
                errmsg = _("No default repository defined")
        except TracError, e:
            errmsg = to_unicode(e)
예제 #7
0
파일: web_ui.py 프로젝트: lkraav/trachacks
def _get_repository(env, req):
    '''From env and req identify and return (reponame, repository, path), 
    removing reponame from path in the process.
    '''
    path = req.args.get('path')
    repo_mgr = RepositoryManager(env)
    reponame, repos, path = repo_mgr.get_repository_by_path(path)
    return reponame, repos, path
예제 #8
0
def _get_repository(env, req):
    '''From env and req identify and return (reponame, repository, path), 
    removing reponame from path in the process.
    '''
    path = req.args.get('path')
    repo_mgr = RepositoryManager(env)
    reponame, repos, path = repo_mgr.get_repository_by_path(path)
    return reponame, repos, path
예제 #9
0
 def _get_source(self, formatter, source_obj, dest_format, start, end, lineno):
     repos_mgr = RepositoryManager(self.env)
     repos_name, repos, source_obj = \
         repos_mgr.get_repository_by_path(source_obj)
     path, rev = _split_path(source_obj)
     try:
         node = repos.get_node(path, rev)
     except (NoSuchChangeset, NoSuchNode), e:
         return system_message(e), None, None, None
예제 #10
0
파일: log.py 프로젝트: pkdevbox/trac
    def _format_link(self, formatter, ns, match, label, fullmatch=None):
        if ns == "log1":
            groups = fullmatch.groupdict()
            it_log = groups.get("it_log")
            revs = groups.get("log_revs")
            path = groups.get("log_path") or "/"
            target = "%s%s@%s" % (it_log, path, revs)
            # prepending it_log is needed, as the helper expects it there
            intertrac = formatter.shorthand_intertrac_helper("log", target, label, fullmatch)
            if intertrac:
                return intertrac
            path, query, fragment = formatter.split_link(path)
        else:
            assert ns in ("log", "log2")
            if ns == "log":
                match, query, fragment = formatter.split_link(match)
            else:
                query = fragment = ""
                match = "".join(reversed(match.split("/", 1)))
            path = match
            revs = ""
            if self.LOG_LINK_RE.match(match):
                indexes = [sep in match and match.index(sep) for sep in ":@"]
                idx = min([i for i in indexes if i is not False])
                path, revs = match[:idx], match[idx + 1 :]

        rm = RepositoryManager(self.env)
        try:
            reponame, repos, path = rm.get_repository_by_path(path)
            if not reponame:
                reponame = rm.get_default_repository(formatter.context)
                if reponame is not None:
                    repos = rm.get_repository(reponame)

            if repos:
                if "LOG_VIEW" in formatter.perm:
                    reponame = repos.reponame or None
                    path = path or "/"
                    revranges = RevRanges(repos, revs)
                    if revranges.has_ranges():
                        href = formatter.href.log(reponame, path, revs=unicode(revranges))
                    else:
                        # try to resolve if single rev
                        repos.normalize_rev(revs)
                        href = formatter.href.log(reponame, path, rev=revs or None)
                    if query and "?" in href:
                        query = "&" + query[1:]
                    return tag.a(label, class_="source", href=href + query + fragment)
                errmsg = _("No permission to view change log")
            elif reponame:
                errmsg = _("Repository '%(repo)s' not found", repo=reponame)
            else:
                errmsg = _("No default repository defined")
        except TracError as e:
            errmsg = to_unicode(e)
        return tag.a(label, class_="missing source", title=errmsg)
예제 #11
0
    def process_request(self, req):
        req.perm.require('BROWSER_VIEW')

        presel = req.args.get('preselected')
        if presel and (presel + '/').startswith(req.href.browser() + '/'):
            req.redirect(presel)

        path = req.args.get('path', '/')
        rev = req.args.get('rev', '')
        if rev in ('', 'HEAD'):
            rev = None
        order = req.args.get('order', 'name').lower()
        desc = req.args.has_key('desc')
        xhr = req.get_header('X-Requested-With') == 'XMLHttpRequest'

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

        # Repository index
        show_index = not reponame and path == '/'
        if show_index:
            if repos and (as_bool(all_repositories[''].get('hidden'))
                          or not repos.can_view(req.perm)):
                repos = None

        if not repos and reponame:
            raise ResourceNotFound(
                _("Repository '%(repo)s' not found", repo=reponame))

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

        # Find node for the requested path/rev
        context = Context.from_request(req)
        node = None
        display_rev = lambda rev: rev
        if repos:
            try:
                if rev:
                    rev = repos.normalize_rev(rev)
                # If `rev` is `None`, we'll try to reuse `None` consistently,
                # as a special shortcut to the latest revision.
                rev_or_latest = rev or repos.youngest_rev
                node = get_existing_node(req, repos, path, rev_or_latest)
            except NoSuchChangeset, e:
                raise ResourceNotFound(e.message,
                                       _('Invalid changeset number'))

            context = context(
                repos.resource.child('source', path, version=rev_or_latest))
            display_rev = repos.display_rev
예제 #12
0
 def _format_link(self, formatter, ns, target, label, fullmatch=None):
     """ returns a tag for
         Resource: Projectname/path/to/src/org/example/package/Class.java Line: 123 """
     # search repository
     rm = RepositoryManager(self.env)
     line = fullmatch.group('line')
     line = line and '#L%s' % line or ""
     # option 1: search with unmodified path
     path = fullmatch.group('path')
     reponame, repos, npath = rm.get_repository_by_path(
         path)  # @UnusedVariable
     node = get_allowed_node(repos, npath, None, formatter.perm)
     if node:
         return tag.a(label,
                      href=formatter.href.browser(path) + line,
                      class_="source")
     # option 2: search with "/trunk/" + path
     path = "/trunk/" + fullmatch.group('path')
     reponame, repos, npath = rm.get_repository_by_path(
         path)  # @UnusedVariable
     node = get_allowed_node(repos, npath, None, formatter.perm)
     if node:
         return tag.a(label,
                      href=formatter.href.browser(path) + line,
                      class_="source")
     # option 3: heuristic search in repositories for subversion
     projectname, trailing = fullmatch.group('path').lstrip('/').split(
         '/', 1)
     psf = self.get_psf()
     if projectname in psf:
         # subversion can checkout in the middle of repository
         path = psf[projectname] + '/' + trailing
         repos = rm.get_all_repositories()
         for npath, reponame in product(self.iter_lstrip(path), repos):
             repo = rm.get_repository(reponame)
             node = get_allowed_node(repo, npath, None, formatter.perm)
             if node:
                 return tag.a(label,
                              class_="source",
                              href=formatter.href.browser(repo.reponame +
                                                          '/' + node.path) +
                              line)
     return tag.a(label, class_='missing source')
예제 #13
0
파일: browser.py 프로젝트: zjj/trac_hack
    def process_request(self, req):
        req.perm.require('BROWSER_VIEW')

        presel = req.args.get('preselected')
        if presel and (presel + '/').startswith(req.href.browser() + '/'):
            req.redirect(presel)

        path = req.args.get('path', '/')
        rev = req.args.get('rev', '')
        if rev in ('', 'HEAD'):
            rev = None
        order = req.args.get('order', 'name').lower()
        desc = req.args.has_key('desc')
        xhr = req.get_header('X-Requested-With') == 'XMLHttpRequest'
        
        rm = RepositoryManager(self.env)
        all_repositories = rm.get_all_repositories()
        reponame, repos, path = rm.get_repository_by_path(path)

        # Repository index
        show_index = not reponame and path == '/'
        if show_index:
            if repos and (as_bool(all_repositories[''].get('hidden'))
                          or not repos.can_view(req.perm)):
                repos = None

        if not repos and reponame:
            raise ResourceNotFound(_("Repository '%(repo)s' not found",
                                     repo=reponame))

        if reponame and reponame != repos.reponame: # Redirect alias
            qs = req.query_string
            req.redirect(req.href.browser(repos.reponame or None, path)
                         + (qs and '?' + qs or ''))
        reponame = repos and repos.reponame or None
        
        # Find node for the requested path/rev
        context = Context.from_request(req)
        node = None
        display_rev = lambda rev: rev
        if repos:
            try:
                if rev:
                    rev = repos.normalize_rev(rev)
                # If `rev` is `None`, we'll try to reuse `None` consistently,
                # as a special shortcut to the latest revision.
                rev_or_latest = rev or repos.youngest_rev
                node = get_existing_node(req, repos, path, rev_or_latest)
            except NoSuchChangeset, e:
                raise ResourceNotFound(e.message,
                                       _('Invalid changeset number'))

            context = context(repos.resource.child('source', path,
                                                   version=rev_or_latest))
            display_rev = repos.display_rev
예제 #14
0
    def process_request(self, req):
        if req.method != 'POST':
            msg = u'Method not allowed (%s)\n' % req.method
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 405)

        path = req.args['path']

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

        if path != '/':
            msg = u'No such repository (%s)\n' % path
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        output = u'Running hook on %s\n' % (reponame or '(default)')

        output += u'* Updating clone\n'
        repos.git.repo.remote('update', '--prune')
        output += u'* Synchronizing with clone\n'
        repos.git.sync()

        try:
            payload = json.loads(req.args['payload'])
            revs = [
                commit['id'] for commit in payload['commits']
                if commit['distinct']
            ]
        except (ValueError, KeyError):
            msg = u'Invalid payload\n'
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        branches = self.get_branches(reponame)
        added, skipped, unknown = classify_commits(revs, repos, branches)

        if added:
            output += u'* Adding %s\n' % describe_commits(added)
            # This is where Trac gets notified of the commits in the changeset
            rm.notify('changeset_added', reponame, added)

        if skipped:
            output += u'* Skipping %s\n' % describe_commits(skipped)

        if unknown:
            output += u'* Unknown %s\n' % describe_commits(unknown)
            self.log.error(u'Payload contains unknown %s',
                           describe_commits(unknown))

        for line in output.splitlines():
            self.log.debug(line)

        req.send(output.encode('utf-8'), 'text/plain', 200 if output else 204)
예제 #15
0
 def _get_source(self, formatter, source_obj, dest_format):
     repos_mgr = RepositoryManager(self.env)
     try:  # 0.12+
         repos_name, repos, source_obj = \
             repos_mgr.get_repository_by_path(source_obj)
     except AttributeError:  # 0.11
         repos = repos_mgr.get_repository(formatter.req.authname)
     path, rev = _split_path(source_obj)
     try:
         node = repos.get_node(path, rev)
     except (NoSuchChangeset, NoSuchNode), e:
         return system_message(e), None, None
예제 #16
0
파일: github.py 프로젝트: gijs/trac-github
    def process_request(self, req):
        rev = req.args.get("rev")
        path = req.args.get("path")

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

        try:
            rev = repos.normalize_rev(rev)
        except NoSuchChangeset, e:
            raise ResourceNotFound(e.message, _("Invalid Changeset Number"))
예제 #17
0
파일: browser.py 프로젝트: pkdevbox/trac
 def _get_link_info(self, path, rev, href, perm):
     rm = RepositoryManager(self.env)
     node = raw_href = title = None
     try:
         reponame, repos, npath = rm.get_repository_by_path(path)
         node = get_allowed_node(repos, npath, rev, perm)
         if node is not None:
             raw_href = self._get_download_href(href, repos, node, rev)
             title = _("Download") if node.isfile else _("Download as Zip archive")
     except TracError:
         pass
     return (node, raw_href, title)
예제 #18
0
    def process_request(self, req):
        if req.method != 'POST':
            msg = u'Method not allowed (%s)\n' % req.method
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 405)

        path = req.args['path']

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

        if path != '/':
            msg = u'No such repository (%s)\n' % path
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        output = u'Running hook on %s\n' % (reponame or '(default)')

        output += u'* Updating clone\n'
        repos.git.repo.remote('update', '--prune')
        output += u'* Synchronizing with clone\n'
        repos.git.sync()

        try:
            payload = json.loads(req.args['payload'])
            revs = [commit['id']
                    for commit in payload['commits'] if commit['distinct']]
        except (ValueError, KeyError):
            msg = u'Invalid payload\n'
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        branches = self.get_branches(reponame)
        added, skipped, unknown = classify_commits(revs, repos, branches)

        if added:
            output += u'* Adding %s\n' % describe_commits(added)
            # This is where Trac gets notified of the commits in the changeset
            rm.notify('changeset_added', reponame, added)

        if skipped:
            output += u'* Skipping %s\n' % describe_commits(skipped)

        if unknown:
            output += u'* Unknown %s\n' % describe_commits(unknown)
            self.log.error(u'Payload contains unknown %s',
                    describe_commits(unknown))

        for line in output.splitlines():
            self.log.debug(line)

        req.send(output.encode('utf-8'), 'text/plain', 200 if output else 204)
예제 #19
0
 def _get_link_info(self, path, rev, href, perm):
     rm = RepositoryManager(self.env)
     node = raw_href = title = None
     try:
         reponame, repos, npath = rm.get_repository_by_path(path)
         node = get_allowed_node(repos, npath, rev, perm)
         if node is not None:
             raw_href = self._get_download_href(href, repos, node, rev)
             title = _("Download") if node.isfile \
                     else _("Download as Zip archive")
     except TracError:
         pass
     return node, raw_href, title
예제 #20
0
    def process_request(self, req):
        if not self.gh_repo:
            return super(GitHubBrowser, self).process_request(req)

        rev = req.args.get('rev')
        path = req.args.get('path')
        rm = RepositoryManager(self.env)
        reponame, repos, path = rm.get_repository_by_path(path)

        try:
            rev = repos.normalize_rev(rev)
        except NoSuchChangeset, e:
            raise ResourceNotFound(e.message, _('Invalid Changeset Number'))
예제 #21
0
파일: github.py 프로젝트: gijs/trac-github
    def process_request(self, req):
        if req.method != "POST":
            msg = u"Method not allowed (%s)\n" % req.method
            self.log.warning(msg.rstrip("\n"))
            req.send(msg.encode("utf-8"), "text/plain", 405)

        path = req.args["path"]

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

        if path != "/":
            msg = u"No such repository (%s)\n" % path
            self.log.warning(msg.rstrip("\n"))
            req.send(msg.encode("utf-8"), "text/plain", 400)

        output = u"Running hook on %s\n" % (reponame or "(default)")

        output += u"* Updating clone\n"
        output += repos.git.repo.remote("update", "--prune")

        try:
            payload = json.loads(req.args["payload"])
            revs = [commit["id"] for commit in payload["commits"] if commit["distinct"]]
        except (ValueError, KeyError):
            msg = u"Invalid payload\n"
            self.log.warning(msg.rstrip("\n"))
            req.send(msg.encode("utf-8"), "text/plain", 400)

        branches = self.get_branches(reponame)
        added_revs, skipped_revs = [], []
        for rev in revs:
            if rev_in_branches(repos.get_changeset(rev), branches):
                added_revs.append(rev)
            else:
                skipped_revs.append(rev)

        if added_revs:
            output += u"* Adding %s\n" % describe_commits(added_revs)
            # This is where Trac gets notified of the commits in the changeset
            rm.notify("changeset_added", reponame, added_revs)

        if skipped_revs:
            output += u"* Skipping %s\n" % describe_commits(skipped_revs)

        for line in output.splitlines():
            self.log.debug(line)

        req.send(output.encode("utf-8"), "text/plain", 200 if output else 204)
예제 #22
0
    def process_request(self, req):
        rev = req.args.get('rev')
        path = req.args.get('path')

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

        rev = repos.normalize_rev(rev)

        if path and path != '/':
            path = path.lstrip('/')
            # GitHub will s/blob/tree/ if the path is a directory
            url = 'https://github.com/%s/blob/%s/%s' % (gh_repo, rev, path)
        else:
            url = 'https://github.com/%s/commit/%s' % (gh_repo, rev)
        req.redirect(url)
예제 #23
0
    def process_request(self, req):
        if not self.repository:                             # pragma: no cover
            return super(GitHubBrowser, self).process_request(req)

        rev = req.args.get('rev')
        path = req.args.get('path')

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

        key = 'repository' if is_default(reponame) else '%s.repository' % reponame
        gh_repo = self.config.get('github', key)

        try:
            rev = repos.normalize_rev(rev)
        except NoSuchChangeset, e:
            raise ResourceNotFound(e.message, _('Invalid Changeset Number'))
예제 #24
0
    def process_request(self, req):
        rev = req.args.get('rev')
        path = req.args.get('path')

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

        rev = repos.normalize_rev(rev)

        if path and path != '/':
            path = path.lstrip('/')
            # GitHub will s/blob/tree/ if the path is a directory
            url = 'https://github.com/%s/blob/%s/%s' % (gh_repo, rev, path)
        else:
            url = 'https://github.com/%s/commit/%s' % (gh_repo, rev)
        req.redirect(url)
예제 #25
0
 def expand_macro(self, formatter, name, text, args=None):
     rm = RepositoryManager(self.env)
     reponame, repos, path = rm.get_repository_by_path(
         "/AROS/trunk/AROS/workbench/system/AboutAROS/db/credits")
     node = repos.get_node(path, repos.youngest_rev)
     stream = node.get_content()
     content = stream.read(10000)
     content = unicode(content, "iso8859-15")
     content = content.split("\n")
     result = ""
     listopen = False
     for line in content:
         if ":" in line:
             if listopen:
                 result = result + "</ul>\n"
             result = result + "<h2> " + line + "</h2>" + "\n<ul>\n"
             listopen = True
         elif line.strip() != "":
             result = result + "<li>" + line + "</li>\n"
     if listopen:
         result = result + "</ul>\n"
     return result
예제 #26
0
    def process_request(self, req):
        path = req.args['path']

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

        if repos is None or path != '/':
            msg = u'No such repository (%s)\n' % path
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        if req.method != 'POST':
            msg = u'Endpoint is ready to accept GitHub notifications.\n'
            self.log.warning(u'Method not allowed (%s)', req.method)
            req.send(msg.encode('utf-8'), 'text/plain', 405)

        # Verify the event's signature
        reqdata = req.read()
        signature = req.get_header('X-Hub-Signature')
        if not self._verify_webhook_signature(signature, reqdata):
            msg = u'Webhook signature verification failed\n'
            self.log.warning(msg.rstrip('\n')) # pylint: disable=no-member
            req.send(msg.encode('utf-8'), 'text/plain', 403)

        event = req.get_header('X-GitHub-Event')
        if event == 'ping':
            payload = json.loads(reqdata)
            req.send(payload['zen'].encode('utf-8'), 'text/plain', 200)
        elif event != 'push':
            msg = u'Only ping and push are supported\n'
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        output = u'Running hook on %s\n' % (reponame or '(default)')

        output += u'* Updating clone\n'
        try:
            git = repos.git.repo             # GitRepository
        except AttributeError:
            git = repos.repos.git.repo       # GitCachedRepository
        git.remote('update', '--prune')

        # Ensure that repos.get_changeset can find the new changesets.
        output += u'* Synchronizing with clone\n'
        repos.sync()

        try:
            payload = json.loads(reqdata)
            revs = [commit['id']
                    for commit in payload['commits'] if commit['distinct']]
        except (ValueError, KeyError):
            msg = u'Invalid payload\n'
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        branches = self.get_branches(reponame)
        added, skipped, unknown = classify_commits(revs, repos, branches)

        if added:
            output += u'* Adding %s\n' % describe_commits(added)
            # This is where Trac gets notified of the commits in the changeset
            rm.notify('changeset_added', reponame, added)

        if skipped:
            output += u'* Skipping %s\n' % describe_commits(skipped)

        if unknown:
            output += u'* Unknown %s\n' % describe_commits(unknown)
            self.log.error(u'Payload contains unknown %s',
                    describe_commits(unknown))

        status = 200

        git_dir = git.rev_parse('--git-dir').rstrip('\n')
        hook = os.path.join(git_dir, 'hooks', 'trac-github-update')
        if os.path.isfile(hook):
            output += u'* Running trac-github-update hook\n'
            try:
                p = Popen(hook, cwd=git_dir,
                          stdin=PIPE, stdout=PIPE, stderr=STDOUT,
                          close_fds=trac.util.compat.close_fds)
            except Exception as e:
                output += u'Error: hook execution failed with exception\n%s' % (traceback.format_exc(),)
                status = 500
            else:
                hookoutput = p.communicate(input=reqdata)[0]
                output += hookoutput.decode('utf-8')
                if p.returncode != 0:
                    output += u'Error: hook failed with exit code %d\n' % (p.returncode,)
                    status = 500

        for line in output.splitlines():
            self.log.debug(line)

        if status == 200 and not output:
            status = 204

        req.send(output.encode('utf-8'), 'text/plain', status)
예제 #27
0
class RepositoryManager(Component):
    """Adds creation, modification and deletion of repositories.

    This class extends Trac's `RepositoryManager` and adds some
    capabilities that allow users to create and manage repositories.
    The original `RepositoryManager` *just* allows adding and removing
    existing repositories from Trac's database, which means that still
    someone must do some shell work on the server.

    To work nicely together with manually created and added repositories
    a new `ManagedRepository` class is used to mark the ones that can be
    handled by this module. It also implements forking, if the connector
    supports that, which creates instances of `ForkedRepository`.
    """

    base_dir = Option('repository-manager',
                      'base_dir',
                      'repositories',
                      doc="""The base folder in which repositories will be
                             created.
                             """)
    owner_as_maintainer = BoolOption('repository-manager',
                                     'owner_as_maintainer',
                                     True,
                                     doc="""If true, the owner will have the
                                            role of a maintainer, too.
                                            Otherwise, he will only act as an
                                            administrator for his repositories.
                                            """)

    connectors = ExtensionPoint(IAdministrativeRepositoryConnector)

    manager = None

    roles = ('maintainer', 'writer', 'reader')

    def __init__(self):
        self.manager = TracRepositoryManager(self.env)

    def get_supported_types(self):
        """Return the list of supported repository types."""
        types = set(type for connector in self.connectors
                    for (type, prio) in connector.get_supported_types() or []
                    if prio >= 0)
        return list(types & set(self.manager.get_supported_types()))

    def get_forkable_types(self):
        """Return the list of forkable repository types."""
        return list(type for type in self.get_supported_types()
                    if self.can_fork(type))

    def can_fork(self, type):
        """Return whether the given repository type can be forked."""
        return self._get_repository_connector(type).can_fork(type)

    def can_delete_changesets(self, type):
        """Return whether the given repository type can delete changesets."""
        return self._get_repository_connector(type).can_delete_changesets(type)

    def can_ban_changesets(self, type):
        """Return whether the given repository type can ban changesets."""
        return self._get_repository_connector(type).can_ban_changesets(type)

    def get_forkable_repositories(self):
        """Return a dictionary of repository information, indexed by
        name and including only repositories that can be forked."""
        repositories = self.manager.get_all_repositories()
        result = {}
        for key in repositories:
            if repositories[key]['type'] in self.get_forkable_types():
                result[key] = repositories[key]['name']
        return result

    def get_managed_repositories(self):
        """Return the list of existing managed repositories."""
        repositories = self.manager.get_all_repositories()
        result = {}
        for key in repositories:
            try:
                self.get_repository(repositories[key]['name'], True)
                result[key] = repositories[key]['name']
            except:
                pass
        return result

    def get_repository(self, name, convert_to_managed=False):
        """Retrieve the appropriate repository for the given name.

        Converts the found repository into a `ManagedRepository`, if
        requested. In that case, expect an exception if the found
        repository was not created using this `RepositoryManager`.
        """
        repo = self.manager.get_repository(name)
        if repo and convert_to_managed:
            convert_managed_repository(self.env, repo)
        return repo

    def get_repository_by_id(self, id, convert_to_managed=False):
        """Retrieve a matching `Repository` for the given id."""
        repositories = self.manager.get_all_repositories()
        for name, info in repositories.iteritems():
            if info['id'] == int(id):
                return self.get_repository(name, convert_to_managed)
        return None

    def get_repository_by_path(self, path):
        """Retrieve a matching `Repository` for the given path."""
        return self.manager.get_repository_by_path(path)

    def get_base_directory(self, type):
        """Get the base directory for the given repository type."""
        return os.path.join(self.env.path, self.base_dir, type)

    def create(self, repo):
        """Create a new empty repository.

         * Checks if the new repository can be created and added
         * Prepares the filesystem
         * Uses an appropriate connector to create and initialize the
           repository
         * Postprocesses the filesystem (modes)
         * Inserts everything into the database and synchronizes Trac
        """
        if self.get_repository(repo['name']) or os.path.lexists(repo['dir']):
            raise TracError(_("Repository or directory already exists."))

        self._prepare_base_directory(repo['dir'])

        self._get_repository_connector(repo['type']).create(repo)

        self._adjust_modes(repo['dir'])

        with self.env.db_transaction as db:
            id = self.manager.get_repository_id(repo['name'])
            roles = list((id, role + 's', '') for role in self.roles)
            db.executemany(
                "INSERT INTO repository (id, name, value) VALUES (%s, %s, %s)",
                [(id, 'dir', repo['dir']), (id, 'type', repo['type']),
                 (id, 'owner', repo['owner'])] + roles)
            self.manager.reload_repositories()
        self.manager.get_repository(repo['name']).sync(None, True)
        self.update_auth_files()

    def fork_local(self, repo):
        """Fork a local repository.

         * Checks if the new repository can be created and added
         * Checks if the origin exists and can be forked
         * The filesystem is obviously already prepared
         * Uses an appropriate connector to fork the repository
         * Postprocesses the filesystem (modes)
         * Inserts everything into the database and synchronizes Trac
        """
        if self.get_repository(repo['name']) or os.path.lexists(repo['dir']):
            raise TracError(_("Repository or directory already exists."))

        origin = self.get_repository(repo['origin'], True)
        if not origin:
            raise TracError(_("Origin for local fork does not exist."))
        if origin.type != repo['type']:
            raise TracError(
                _("Fork of local repository must have same type "
                  "as origin."))
        repo.update({'origin_url': 'file://' + origin.directory})

        self._prepare_base_directory(repo['dir'])

        self._get_repository_connector(repo['type']).fork(repo)

        self._adjust_modes(repo['dir'])

        with self.env.db_transaction as db:
            id = self.manager.get_repository_id(repo['name'])
            roles = list((id, role + 's', '') for role in self.roles)
            db.executemany(
                "INSERT INTO repository (id, name, value) VALUES (%s, %s, %s)",
                [(id, 'dir', repo['dir']), (id, 'type', repo['type']),
                 (id, 'owner', repo['owner']),
                 (id, 'description', origin.description),
                 (id, 'origin', origin.id),
                 (id, 'inherit_readers', True)] + roles)
            self.manager.reload_repositories()
        self.manager.get_repository(repo['name']).sync(None, True)
        self.update_auth_files()

    def modify(self, repo, data):
        """Modify an existing repository."""
        convert_managed_repository(self.env, repo)
        if repo.directory != data['dir']:
            shutil.move(repo.directory, data['dir'])
        with self.env.db_transaction as db:
            db.executemany(
                "UPDATE repository SET value = %s WHERE id = %s AND name = %s",
                [(data[key], repo.id, key) for key in data])
            self.manager.reload_repositories()
        if repo.directory != data['dir']:
            repo = self.get_repository(data['name'])
            repo.sync(clean=True)
        self.update_auth_files()

    def remove(self, repo, delete):
        """Remove an existing repository.

        Depending on the parameter delete this method also removes the
        repository from the filesystem. This can not be undone.
        """
        convert_managed_repository(self.env, repo)
        if delete:
            shutil.rmtree(repo.directory)
        with self.env.db_transaction as db:
            db("DELETE FROM repository WHERE id = %d" % repo.id)
            db("DELETE FROM revision WHERE repos = %d" % repo.id)
            db("DELETE FROM node_change WHERE repos = %d" % repo.id)
        self.manager.reload_repositories()
        self.update_auth_files()

    def delete_changeset(self, repo, rev, ban):
        """Delete a changeset from a managed repository, if supported.

        Depending on the parameter ban this method also marks the
        changeset to be kept out of the repository. That features needs
        special support by the used scm.
        """
        convert_managed_repository(self.env, repo)
        self._get_repository_connector(repo.type).delete_changeset(
            repo, rev, ban)

    def add_role(self, repo, role, subject):
        """Add a role for the given repository."""
        assert role in self.roles
        convert_managed_repository(self.env, repo)
        role_attr = '_' + role + 's'
        setattr(repo, role_attr, getattr(repo, role_attr) | set([subject]))
        self._update_roles_in_db(repo)

    def revoke_roles(self, repo, roles):
        """Revoke a list of `role, subject` pairs."""
        convert_managed_repository(self.env, repo)
        for role, subject in roles:
            role_attr = '_' + role + 's'
            config = getattr(repo, role_attr)
            config = config - set([subject])
            setattr(repo, role_attr, getattr(repo, role_attr) - set([subject]))
        self._update_roles_in_db(repo)

    def update_auth_files(self):
        """Rewrites all configured auth files for all managed
        repositories.
        """
        types = self.get_supported_types()
        all_repositories = []
        for repo in self.manager.get_real_repositories():
            try:
                convert_managed_repository(self.env, repo)
                all_repositories.append(repo)
            except:
                pass
        for type in types:
            repos = [repo for repo in all_repositories if repo.type == type]
            self._get_repository_connector(type).update_auth_files(repos)

        authz_source_file = AuthzSourcePolicy(self.env).authz_file
        if authz_source_file:
            authz_source_path = os.path.join(self.env.path, authz_source_file)

            authz = ConfigParser()

            groups = set()
            for repo in all_repositories:
                groups |= {
                    name
                    for name in repo.maintainers() if name[0] == '@'
                }
                groups |= {name for name in repo.writers() if name[0] == '@'}
                groups |= {name for name in repo.readers() if name[0] == '@'}

            authz.add_section('groups')
            for group in groups:
                members = expand_user_set(self.env, [group])
                authz.set('groups', group[1:], ', '.join(sorted(members)))
            authenticated = sorted({u[0] for u in self.env.get_known_users()})
            authz.set('groups', 'authenticated', ', '.join(authenticated))

            for repo in all_repositories:
                section = repo.reponame + ':/'
                authz.add_section(section)
                r = repo.maintainers() | repo.writers() | repo.readers()

                def apply_user_list(users, action):
                    if not users:
                        return
                    if 'anonymous' in users:
                        authz.set(section, '*', action)
                        return
                    if 'authenticated' in users:
                        authz.set(section, '@authenticated', action)
                        return
                    for user in sorted(users):
                        authz.set(section, user, action)

                apply_user_list(r, 'r')

            self._prepare_base_directory(authz_source_path)
            with open(authz_source_path, 'wb') as authz_file:
                authz.write(authz_file)
            try:
                modes = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP
                os.chmod(authz_source_path, modes)
            except:
                pass

    ### Private methods
    def _get_repository_connector(self, repo_type):
        """Get the matching connector with maximum priority."""
        return max(((connector, type, prio) for connector in self.connectors
                    for (type, prio) in connector.get_supported_types()
                    if prio >= 0 and type == repo_type),
                   key=lambda x: x[2])[0]

    def _prepare_base_directory(self, directory):
        """Create the base directories and set the correct modes."""
        base = os.path.dirname(directory)
        original_umask = os.umask(0)
        try:
            os.makedirs(base, stat.S_IRWXU | stat.S_IRWXG)
        except OSError, e:
            if e.errno == errno.EEXIST and os.path.isdir(base):
                pass
            else:
                raise
        finally:
예제 #28
0
    def _format_name(self, req, url):
        linkname = url
        name = ""
        missing = False

        path_info = url
        query_string = ''
        idx = path_info.find('?')
        if idx >= 0:
            path_info, query_string = path_info[:idx], path_info[idx:]
        href = req.href(path_info) + query_string

        args = arg_list_to_args(parse_arg_list(query_string.lstrip('?')))
        version = args.get('version', False)

        path = path_info.strip('/').split('/')
        realm = path[0]
        class_ = realm
        if len(path) > 1:
            resource = Resource(realm, path[1])
            if resource:
                if realm == 'ticket':
                    linkname = get_resource_shortname(self.env, resource)
                    try:
                        name = get_resource_summary(self.env, resource)
                    except ResourceNotFound:
                        missing = True
                    else:
                        from trac.ticket.model import Ticket
                        class_ = Ticket(self.env, resource.id)['status'] + \
                            ' ' + class_
                elif realm == 'milestone':
                    linkname = get_resource_name(self.env, resource)
                elif realm == 'wiki':
                    resource = Resource(realm, '/'.join(path[1:]), version)
                    linkname = get_resource_shortname(self.env, resource)
                    if version:
                        linkname += '@' + version
                elif realm == 'report':
                    linkname = "{%s}" % path[1]
                    name = self._format_report_name(path[1])
                elif realm == 'changeset':
                    rev = path[1]
                    parent = Resource('source', '/'.join(path[2:]))
                    resource = Resource(realm, rev, False, parent)
                    linkname = "[%s]" % rev
                    name = get_resource_description(self.env, resource)
                elif realm == 'browser':
                    rm = RepositoryManager(self.env)
                    reponame, repos, path = rm.get_repository_by_path('/'.join(
                        path[1:]))
                    parent = Resource('source', reponame)
                    resource = Resource('source', path, False, parent)
                    linkname = get_resource_description(self.env, resource)
                    name = get_resource_summary(self.env, resource)
                elif realm == 'attachment':
                    # Assume a file and check existence
                    parent = Resource(path[1], '/'.join(path[2:-1]))
                    resource = Resource(realm, path[-1], parent=parent)
                    linkname = get_resource_name(self.env, resource)
                    if not resource_exists(self.env, resource):
                        # Assume an attachment list page and check existence
                        parent = Resource(path[1], '/'.join(path[2:]))
                        if resource_exists(self.env, parent):
                            resource = Resource(realm, parent=parent)
                            linkname = get_resource_name(self.env, resource)
                            if not query_string:
                                # Trailing slash needed for Trac < 1.0, t:#10280
                                href += '/'
                        else:
                            # Assume it's a missing attachment
                            missing = True
                else:
                    linkname = get_resource_shortname(self.env, resource)
                    name = get_resource_summary(self.env, resource)
        elif len(path) == 1 and path[0] and path[0] != 'wiki':
            linkname = path[0].capitalize()
        else:
            class_ = 'wiki'
            linkname = 'WikiStart'

        if missing:
            href = None
            class_ = 'missing ' + realm

        return {
            'class_': class_,
            'href': href,
            'linkname': linkname,
            'name': name,
            'delete': req.href.bookmark('delete_in_page', url),
        }
예제 #29
0
    def process_request(self, req):
        path = req.args['path']

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

        if repos is None or path != '/':
            msg = u'No such repository (%s)\n' % path
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        if req.method != 'POST':
            msg = u'Endpoint is ready to accept GitHub notifications.\n'
            self.log.warning(u'Method not allowed (%s)' % req.method)
            req.send(msg.encode('utf-8'), 'text/plain', 405)

        event = req.get_header('X-GitHub-Event')
        if event == 'ping':
            payload = json.loads(req.read())
            req.send(payload['zen'].encode('utf-8'), 'text/plain', 200)
        elif event != 'push':
            msg = u'Only ping and push are supported\n'
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        output = u'Running hook on %s\n' % (reponame or '(default)')

        output += u'* Updating clone\n'
        try:
            git = repos.git.repo  # GitRepository
        except AttributeError:
            git = repos.repos.git.repo  # GitCachedRepository
        git.remote('update', '--prune')

        # Ensure that repos.get_changeset can find the new changesets.
        output += u'* Synchronizing with clone\n'
        repos.sync()

        try:
            payload = json.loads(req.read())
            revs = [
                commit['id'] for commit in payload['commits']
                if commit['distinct']
            ]
        except (ValueError, KeyError):
            msg = u'Invalid payload\n'
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        branches = self.get_branches(reponame)
        added, skipped, unknown = classify_commits(revs, repos, branches)

        if added:
            output += u'* Adding %s\n' % describe_commits(added)
            # This is where Trac gets notified of the commits in the changeset
            rm.notify('changeset_added', reponame, added)

        if skipped:
            output += u'* Skipping %s\n' % describe_commits(skipped)

        if unknown:
            output += u'* Unknown %s\n' % describe_commits(unknown)
            self.log.error(u'Payload contains unknown %s',
                           describe_commits(unknown))

        for line in output.splitlines():
            self.log.debug(line)

        req.send(output.encode('utf-8'), 'text/plain', 200 if output else 204)
예제 #30
0
파일: log.py 프로젝트: thimalk/bloodhound
    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 = int(req.args.get('limit') or self.default_log_limit)

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

        if not repos:
            raise ResourceNotFound(_("Repository '%(repo)s' not found",
                                   repo=reponame))

        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.
        revranges = None
        if revs:
            try:
                revranges = Ranges(revs)
                rev = revranges.b
            except ValueError:
                pass
        rev = unicode(repos.normalize_rev(rev))
        display_rev = repos.display_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('changeset')
        show_graph = False
        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:
            def history():
                prevpath = path
                expected_next_item = None
                ranges = list(revranges.pairs)
                ranges.reverse()
                for (a, b) in ranges:
                    a = repos.normalize_rev(a)
                    b = repos.normalize_rev(b)
                    while not repos.rev_older_than(b, a):
                        node = get_existing_node(req, repos, prevpath, b)
                        node_history = list(node.get_history(2))
                        p, rev, chg = node_history[0]
                        if repos.rev_older_than(rev, a):
                            break # simply skip, no separator
                        if 'CHANGESET_VIEW' in req.perm(cset_resource(id=rev)):
                            if expected_next_item:
                                # check whether we're continuing previous range
                                np, nrev, nchg = expected_next_item
                                if rev != nrev: # no, we need a separator
                                    yield (np, nrev, None)
                            yield node_history[0]
                        prevpath = node_history[-1][0] # follow copy
                        b = repos.previous_rev(rev)
                        if len(node_history) > 1:
                            expected_next_item = node_history[-1]
                        else:
                            expected_next_item = None
                if expected_next_item:
                    yield (expected_next_item[0], expected_next_item[1], 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
        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
        if 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=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 count >= limit:
            # stop_limit reached, there _might_ be some more
            next_rev = info[-1]['rev']
            next_path = info[-1]['path']
            next_revranges = None
            if revranges:
                next_revranges = str(revranges.truncate(next_rev))
            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=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': 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, 'text/plain'
        elif format == 'rss':
            data['email_map'] = Chrome(self.env).get_email_map()
            data['context'] = web_context(req, 'source',
                                          path, parent=repos.resource,
                                          absurls=True)
            return 'revisionlog.rss', data, '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, None
예제 #31
0
    def expand_macro(self, formatter, name, content):
        req = formatter.req
        args, kwargs = parse_args(content)
        args += [None, None]
        path, limit = args[:2]
        limit = kwargs.pop('limit', limit)
        package = kwargs.pop('package', None)

        if 'CHANGESET_VIEW' not in req.perm:
            return Markup('<i>Releases not available</i>')

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

        rev = repo.get_youngest_rev()
        rev = repo.normalize_rev(rev)
        path = repo.normalize_path(path)
        if limit is None:
            limit = 20
        else:
            limit = int(limit)

        releases = self.get_releases(repo, path, rev)

        # limit the releases after they have been sorted
        releases = releases[:1 + limit]
        items = []
        releases = [None] + releases + [None]

        # some extra checks to avoid using double-slashes
        if reponame == '':
            if path == '/':
                path = ''
            else:
                path = '/' + path
        elif path == '/':
            path = '/' + reponame.rstrip('/')
        else:
            path = '/' + reponame.rstrip('/') + '/' + path.lstrip('/')

        if not package:
            package = path.split("/")[-1]

        for i in xrange(len(releases) - 2):
            prev, cur, next = releases[i:i + 3]

            if prev == None and next == None:
                # no releases yet, just show trunk
                items.append(" * "
                             " [/browser%(path)s/trunk trunk]"
                             " @[changeset:%(rev)s/%(reponame)s %(rev)s]"
                             " ("
                             "[/log%(path)s/trunk changes]"
                             " [/changeset?new_path=%(path)s/trunk diffs]"
                             ")" % {
                                 'reponame': reponame,
                                 'path': path,
                                 'rev': cur['rev'],
                             })
            elif prev == None:
                # first entry = trunk
                items.append(
                    " * "
                    " [/browser%(path)s/trunk trunk]"
                    " @[changeset:%(rev)s/%(reponame)s %(rev)s]"
                    " ("
                    "[/log%(path)s/trunk?revs=%(stop_rev)s-%(rev)s changes]"
                    " [/changeset?old_path=%(path)s/tags/%(old_tag)s&new_path=%(path)s/trunk diffs]"
                    ")" % {
                        'reponame': reponame,
                        'path': path,
                        'rev': cur['rev'],
                        'old_tag': next['version'],
                        'stop_rev': next['rev'],
                    })
            elif next != None:
                # regular releases
                release_page = 'release/%s-%s' % (package, cur['version'])
                page = WikiPage(self.env, release_page)
                if page.exists:
                    release_link = " [wiki:%s release notes]" % (release_page)
                else:
                    release_link = ""

                items.append(
                    " * '''%(date)s'''"
                    " [/log%(path)s/tags/%(new_tag)s %(new_tag)s] "
                    " @[changeset:%(rev)s/%(reponame)s %(rev)s]"
                    " by %(author)s"
                    " ("
                    "[/log%(path)s/trunk?revs=%(stop_rev)s-%(rev)s changes]"
                    " [/changeset?old_path=%(path)s/tags/%(old_tag)s&new_path=%(path)s/tags/%(new_tag)s diffs]"
                    "%(release_link)s"
                    ")" % {
                        'reponame': reponame,
                        'path': path,
                        'date': cur['time'].strftime('%Y-%m-%d'),
                        'rev': cur['rev'],
                        'stop_rev': next['rev'],
                        'old_tag': next['version'],
                        'new_tag': cur['version'],
                        'author': cur['author'],
                        'release_link': release_link,
                    })
                url = self.specurl_annotate(cur)
                if url != None:
                    annotate = " spec: [%s annotate]" % url
                    items.append(annotate)
                    # check also diff link
                    url = self.specurl_diff(cur, next)
                    if url != None:
                        annotate = " [%s diff]" % url
                        items.append(annotate)
            else:
                # last release
                items.append(
                    " * '''%(date)s'''"
                    " [/log%(path)s/tags/%(new_tag)s?rev=%(rev)s&mode=follow_copy %(new_tag)s]"
                    " @[changeset:%(rev)s/%(reponame)s %(rev)s]"
                    " by %(author)s" % {
                        'reponame': reponame,
                        'path': path,
                        'date': cur['time'].strftime('%Y-%m-%d'),
                        'rev': cur['rev'],
                        'new_tag': cur['version'],
                        'author': cur['author'],
                    })

        return '<div class="releases">\n' + to_unicode(
            wiki_to_html("\n".join(items), self.env, req)) + '</div>\n'
예제 #32
0
파일: browser.py 프로젝트: pkdevbox/trac
    def process_request(self, req):
        presel = req.args.get("preselected")
        if presel and (presel + "/").startswith(req.href.browser() + "/"):
            req.redirect(presel)

        path = req.args.get("path", "/")
        rev = req.args.get("rev", "")
        if rev.lower() in ("", "head"):
            rev = None
        format = req.args.get("format")
        order = req.args.get("order", "name").lower()
        desc = "desc" in req.args

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

        # Repository index
        show_index = not reponame and path == "/"
        if show_index:
            if repos and (as_bool(all_repositories[""].get("hidden")) or not repos.is_viewable(req.perm)):
                repos = None

        if not repos and reponame:
            raise ResourceNotFound(_("Repository '%(repo)s' not found", repo=reponame))

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

        # Find node for the requested path/rev
        context = web_context(req)
        node = None
        changeset = None
        display_rev = lambda rev: rev
        if repos:
            try:
                if rev:
                    rev = repos.normalize_rev(rev)
                # If `rev` is `None`, we'll try to reuse `None` consistently,
                # as a special shortcut to the latest revision.
                rev_or_latest = rev or repos.youngest_rev
                node = get_existing_node(req, repos, path, rev_or_latest)
            except NoSuchChangeset as e:
                raise ResourceNotFound(e, _("Invalid changeset number"))
            if node:
                try:
                    # use changeset instance to retrieve branches and tags
                    changeset = repos.get_changeset(node.rev)
                except NoSuchChangeset:
                    pass

            context = context.child(repos.resource.child(self.realm, path, version=rev_or_latest))
            display_rev = repos.display_rev

        # Prepare template data
        path_links = get_path_links(req.href, reponame, path, rev, order, desc)

        repo_data = dir_data = file_data = None
        if show_index:
            repo_data = self._render_repository_index(context, all_repositories, order, desc)
        if node:
            if not node.is_viewable(req.perm):
                raise PermissionError("BROWSER_VIEW" if node.isdir else "FILE_VIEW", node.resource, self.env)
            if node.isdir:
                if format in ("zip",):  # extension point here...
                    self._render_zip(req, context, repos, node, rev)
                    # not reached
                dir_data = self._render_dir(req, repos, node, rev, order, desc)
            elif node.isfile:
                file_data = self._render_file(req, context, repos, node, rev)

        if not repos and not (repo_data and repo_data["repositories"]):
            # If no viewable repositories, check permission instead of
            # repos.is_viewable()
            req.perm.require("BROWSER_VIEW")
            if show_index:
                raise ResourceNotFound(_("No viewable repositories"))
            else:
                raise ResourceNotFound(_("No node %(path)s", path=path))

        quickjump_data = properties_data = None
        if node and not req.is_xhr:
            properties_data = self.render_properties("browser", context, node.get_properties())
            quickjump_data = list(repos.get_quickjump_entries(rev))

        data = {
            "context": context,
            "reponame": reponame,
            "repos": repos,
            "repoinfo": all_repositories.get(reponame or ""),
            "path": path,
            "rev": node and node.rev,
            "stickyrev": rev,
            "display_rev": display_rev,
            "changeset": changeset,
            "created_path": node and node.created_path,
            "created_rev": node and node.created_rev,
            "properties": properties_data,
            "path_links": path_links,
            "order": order,
            "desc": 1 if desc else None,
            "repo": repo_data,
            "dir": dir_data,
            "file": file_data,
            "quickjump_entries": quickjump_data,
            "wiki_format_messages": self.config["changeset"].getbool("wiki_format_messages"),
            "xhr": req.is_xhr,  # Remove in 1.3.1
        }
        if req.is_xhr:  # render and return the content only
            return "dir_entries.html", data, None

        if dir_data or repo_data:
            add_script(req, "common/js/expand_dir.js")
            add_script(req, "common/js/keyboard_nav.js")

        # Links for contextual navigation
        if node:
            if node.isfile:
                prev_rev = repos.previous_rev(rev=node.created_rev, path=node.created_path)
                if prev_rev:
                    href = req.href.browser(reponame, node.created_path, rev=prev_rev)
                    add_link(req, "prev", href, _("Revision %(num)s", num=display_rev(prev_rev)))
                if rev is not None:
                    add_link(req, "up", req.href.browser(reponame, node.created_path))
                next_rev = repos.next_rev(rev=node.created_rev, path=node.created_path)
                if next_rev:
                    href = req.href.browser(reponame, node.created_path, rev=next_rev)
                    add_link(req, "next", href, _("Revision %(num)s", num=display_rev(next_rev)))
                prevnext_nav(req, _("Previous Revision"), _("Next Revision"), _("Latest Revision"))
            else:
                if path != "/":
                    add_link(req, "up", path_links[-2]["href"], _("Parent directory"))
                add_ctxtnav(
                    req, tag.a(_("Last Change"), href=req.href.changeset(node.created_rev, reponame, node.created_path))
                )
            if node.isfile:
                annotate = data["file"]["annotate"]
                if annotate:
                    add_ctxtnav(
                        req,
                        _("Normal"),
                        title=_("View file without annotations"),
                        href=req.href.browser(reponame, node.created_path, rev=rev),
                    )
                if annotate != "blame":
                    add_ctxtnav(
                        req,
                        _("Blame"),
                        title=_(
                            "Annotate each line with the last " "changed revision " "(this can be time consuming...)"
                        ),
                        href=req.href.browser(reponame, node.created_path, rev=rev, annotate="blame"),
                    )
            add_ctxtnav(req, _("Revision Log"), href=req.href.log(reponame, path, rev=rev))
            path_url = repos.get_path_url(path, rev)
            if path_url:
                if path_url.startswith("//"):
                    path_url = req.scheme + ":" + path_url
                add_ctxtnav(req, _("Repository URL"), href=path_url)

        add_stylesheet(req, "common/css/browser.css")
        return "browser.html", data, None
예제 #33
0
 def _get_source(self, formatter, source_obj, dest_format):
     repos_mgr = RepositoryManager(self.env)
     try:  # 0.12+
         repos_name, repos, source_obj = repos_mgr.get_repository_by_path(source_obj)
     except AttributeError, e:  # 0.11
         repos = repos_mgr.get_repository(formatter.req.authname)
예제 #34
0
    def process_request(self, req):
        presel = req.args.get('preselected')
        if presel and (presel + '/').startswith(req.href.browser() + '/'):
            req.redirect(presel)

        path = req.args.get('path', '/')
        rev = req.args.get('rev', '')
        if rev.lower() in ('', 'head'):
            rev = None
        format = req.args.get('format')
        order = req.args.get('order', 'name').lower()
        desc = 'desc' in req.args

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

        # Repository index
        show_index = not reponame and path == '/'
        if show_index:
            if repos and (as_bool(all_repositories[''].get('hidden'))
                          or not repos.is_viewable(req.perm)):
                repos = None

        if not repos and reponame:
            raise ResourceNotFound(_("Repository '%(repo)s' not found",
                                     repo=reponame))

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

        # Find node for the requested path/rev
        context = web_context(req)
        node = None
        changeset = None
        display_rev = lambda rev: rev
        if repos:
            try:
                if rev:
                    rev = repos.normalize_rev(rev)
                # If `rev` is `None`, we'll try to reuse `None` consistently,
                # as a special shortcut to the latest revision.
                rev_or_latest = rev or repos.youngest_rev
                node = get_existing_node(req, repos, path, rev_or_latest)
            except NoSuchChangeset as e:
                raise ResourceNotFound(e, _('Invalid changeset number'))
            if node:
                try:
                    # use changeset instance to retrieve branches and tags
                    changeset = repos.get_changeset(node.rev)
                except NoSuchChangeset:
                    pass

            context = context.child(repos.resource.child(self.realm, path,
                                                   version=rev_or_latest))
            display_rev = repos.display_rev

        # Prepare template data
        path_links = get_path_links(req.href, reponame, path, rev,
                                    order, desc)

        repo_data = dir_data = file_data = None
        if show_index:
            repo_data = self._render_repository_index(
                                        context, all_repositories, order, desc)
        if node:
            if not node.is_viewable(req.perm):
                raise PermissionError('BROWSER_VIEW' if node.isdir else
                                      'FILE_VIEW', node.resource, self.env)
            if node.isdir:
                if format in ('zip',): # extension point here...
                    self._render_zip(req, context, repos, node, rev)
                    # not reached
                dir_data = self._render_dir(req, repos, node, rev, order, desc)
            elif node.isfile:
                file_data = self._render_file(req, context, repos, node, rev)

        if not repos and not (repo_data and repo_data['repositories']):
            # If no viewable repositories, check permission instead of
            # repos.is_viewable()
            req.perm.require('BROWSER_VIEW')
            if show_index:
                raise ResourceNotFound(_("No viewable repositories"))
            else:
                raise ResourceNotFound(_("No node %(path)s", path=path))

        quickjump_data = properties_data = None
        if node and not req.is_xhr:
            properties_data = self.render_properties(
                    'browser', context, node.get_properties())
            quickjump_data = list(repos.get_quickjump_entries(rev))

        data = {
            'context': context, 'reponame': reponame, 'repos': repos,
            'repoinfo': all_repositories.get(reponame or ''),
            'path': path, 'rev': node and node.rev, 'stickyrev': rev,
            'display_rev': display_rev, 'changeset': changeset,
            'created_path': node and node.created_path,
            'created_rev': node and node.created_rev,
            'properties': properties_data,
            'path_links': path_links,
            'order': order, 'desc': 1 if desc else None,
            'repo': repo_data, 'dir': dir_data, 'file': file_data,
            'quickjump_entries': quickjump_data,
            'wiki_format_messages':
                self.config['changeset'].getbool('wiki_format_messages'),
        }
        if req.is_xhr: # render and return the content only
            return 'dir_entries.html', data

        if dir_data or repo_data:
            add_script(req, 'common/js/expand_dir.js')
            add_script(req, 'common/js/keyboard_nav.js')

        # Links for contextual navigation
        if node:
            if node.isfile:
                prev_rev = repos.previous_rev(rev=node.created_rev,
                                              path=node.created_path)
                if prev_rev:
                    href = req.href.browser(reponame,
                                            node.created_path, rev=prev_rev)
                    add_link(req, 'prev', href,
                             _('Revision %(num)s', num=display_rev(prev_rev)))
                if rev is not None:
                    add_link(req, 'up', req.href.browser(reponame,
                                                         node.created_path))
                next_rev = repos.next_rev(rev=node.created_rev,
                                          path=node.created_path)
                if next_rev:
                    href = req.href.browser(reponame, node.created_path,
                                            rev=next_rev)
                    add_link(req, 'next', href,
                             _('Revision %(num)s', num=display_rev(next_rev)))
                prevnext_nav(req, _('Previous Revision'), _('Next Revision'),
                             _('Latest Revision'))
            else:
                if path != '/':
                    add_link(req, 'up', path_links[-2]['href'],
                             _('Parent directory'))
                add_ctxtnav(req, tag.a(_('Last Change'),
                            href=req.href.changeset(node.created_rev, reponame,
                                                    node.created_path)))
            if node.isfile:
                annotate = data['file']['annotate']
                if annotate:
                    add_ctxtnav(req, _('Normal'),
                                title=_('View file without annotations'),
                                href=req.href.browser(reponame,
                                                      node.created_path,
                                                      rev=rev))
                if annotate != 'blame':
                    add_ctxtnav(req, _('Blame'),
                                title=_('Annotate each line with the last '
                                        'changed revision '
                                        '(this can be time consuming...)'),
                                href=req.href.browser(reponame,
                                                      node.created_path,
                                                      rev=rev,
                                                      annotate='blame'))
            add_ctxtnav(req, _('Revision Log'),
                        href=req.href.log(reponame, path, rev=rev))
            path_url = repos.get_path_url(path, rev)
            if path_url:
                if path_url.startswith('//'):
                    path_url = req.scheme + ':' + path_url
                add_ctxtnav(req, _('Repository URL'), href=path_url)

        add_stylesheet(req, 'common/css/browser.css')
        return 'browser.html', data
예제 #35
0
    def process_request(self, req):
        if req.method != 'POST':
            msg = u'Method not allowed (%s)\n' % req.method
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 405)

        path = req.args['path']

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

        key = 'branches' if is_default(reponame) else '%s.branches' % reponame
        branches = self.config.getlist('github', key, sep=' ')

        if path != '/':
            msg = u'No such repository (%s)\n' % path
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        output = u'Running hook on %s\n' % (reponame or '(default)')

        output += u'* Updating clone\n'
        output += repos.git.repo.remote('update', '--prune')

        try:
            payload = json.loads(req.args['payload'])
            revs = [commit['id'] for commit in payload['commits']]
        except (ValueError, KeyError):
            msg = u'Invalid payload\n'
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        if branches:
            added_revs, skipped_revs = [], []
            for rev in revs:
                if allow_revision(rev, repos, branches):
                    added_revs.append(rev)
                else:
                    skipped_revs.append(rev)
        else:
            added_revs, skipped_revs = revs, []

        if added_revs:
            if len(added_revs) == 1:
                output += u'* Adding commit %s\n' % added_revs[0]
            else:
                output += u'* Adding commits %s\n' % u', '.join(added_revs)

            # This is where Trac gets notified of the commits in the changeset
            rm.notify('changeset_added', reponame, added_revs)

        if skipped_revs:
            if len(skipped_revs) == 1:
                output += u'* Skipping commit %s\n' % skipped_revs[0]
            else:
                output += u'* Skipping commits %s\n' % u', '.join(skipped_revs)

        for line in output.splitlines():
            self.log.debug(line)

        req.send(output.encode('utf-8'), 'text/plain', 200 if output else 204)
예제 #36
0
    def process_request(self, req):
        path = req.args['path']

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

        if repos is None or path != '/':
            msg = u'No such repository (%s)\n' % path
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        if req.method != 'POST':
            msg = u'Endpoint is ready to accept GitHub notifications.\n'
            self.log.warning(u'Method not allowed (%s)' % req.method)
            req.send(msg.encode('utf-8'), 'text/plain', 405)

        event = req.get_header('X-GitHub-Event')
        if event == 'ping':
            payload = json.loads(req.read())
            req.send(payload['zen'].encode('utf-8'), 'text/plain', 200)
        elif event != 'push':
            msg = u'Only ping and push are supported\n'
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        output = u'Running hook on %s\n' % (reponame or '(default)')

        output += u'* Updating clone\n'
        try:
            git = repos.git.repo             # GitRepository
        except AttributeError:
            git = repos.repos.git.repo       # GitCachedRepository
        git.remote('update', '--prune')

        # Ensure that repos.get_changeset can find the new changesets.
        output += u'* Synchronizing with clone\n'
        repos.sync()

        try:
            payload = json.loads(req.read())
            revs = [commit['id']
                    for commit in payload['commits'] if commit['distinct']]
        except (ValueError, KeyError):
            msg = u'Invalid payload\n'
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        branches = self.get_branches(reponame)
        added, skipped, unknown = classify_commits(revs, repos, branches)

        if added:
            output += u'* Adding %s\n' % describe_commits(added)
            # This is where Trac gets notified of the commits in the changeset
            rm.notify('changeset_added', reponame, added)

        if skipped:
            output += u'* Skipping %s\n' % describe_commits(skipped)

        if unknown:
            output += u'* Unknown %s\n' % describe_commits(unknown)
            self.log.error(u'Payload contains unknown %s',
                    describe_commits(unknown))

        for line in output.splitlines():
            self.log.debug(line)

        req.send(output.encode('utf-8'), 'text/plain', 200 if output else 204)
예제 #37
0
파일: log.py 프로젝트: pkdevbox/trac
    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 = int(req.args.get("limit") or 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, "text/plain"
        elif format == "rss":
            data["context"] = web_context(req, "source", path, parent=repos.resource, absurls=True)
            return "revisionlog.rss", data, "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, None
예제 #38
0
    def process_request(self, req):
        path = req.args['path']

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

        if repos is None or path != '/':
            msg = u'No such repository (%s)\n' % path
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        if req.method != 'POST':
            msg = u'Endpoint is ready to accept GitHub notifications.\n'
            self.log.warning(u'Method not allowed (%s)', req.method)
            req.send(msg.encode('utf-8'), 'text/plain', 405)

        # Verify the event's signature
        reqdata = req.read()
        signature = req.get_header('X-Hub-Signature')
        if not self._verify_webhook_signature(signature, reqdata):
            msg = u'Webhook signature verification failed\n'
            self.log.warning(msg.rstrip('\n')) # pylint: disable=no-member
            req.send(msg.encode('utf-8'), 'text/plain', 403)

        event = req.get_header('X-GitHub-Event')
        if event == 'ping':
            payload = json.loads(reqdata)
            req.send(payload['zen'].encode('utf-8'), 'text/plain', 200)
        elif event != 'push':
            msg = u'Only ping and push are supported\n'
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        output = u'Running hook on %s\n' % (reponame or '(default)')

        output += u'* Updating clone\n'
        try:
            git = repos.git.repo             # GitRepository
        except AttributeError:
            git = repos.repos.git.repo       # GitCachedRepository
        git.remote('update', '--prune')

        # Ensure that repos.get_changeset can find the new changesets.
        output += u'* Synchronizing with clone\n'
        repos.sync()

        try:
            payload = json.loads(reqdata)
            revs = [commit['id']
                    for commit in payload['commits'] if commit['distinct']]
        except (ValueError, KeyError):
            msg = u'Invalid payload\n'
            self.log.warning(msg.rstrip('\n'))
            req.send(msg.encode('utf-8'), 'text/plain', 400)

        branches = self.get_branches(reponame)
        added, skipped, unknown = classify_commits(revs, repos, branches)

        if added:
            output += u'* Adding %s\n' % describe_commits(added)
            # This is where Trac gets notified of the commits in the changeset
            rm.notify('changeset_added', reponame, added)

        if skipped:
            output += u'* Skipping %s\n' % describe_commits(skipped)

        if unknown:
            output += u'* Unknown %s\n' % describe_commits(unknown)
            self.log.error(u'Payload contains unknown %s',
                    describe_commits(unknown))

        status = 200

        git_dir = git.rev_parse('--git-dir').rstrip('\n')
        hook = os.path.join(git_dir, 'hooks', 'trac-github-update')
        if os.path.isfile(hook):
            output += u'* Running trac-github-update hook\n'
            try:
                p = Popen(hook, cwd=git_dir,
                          stdin=PIPE, stdout=PIPE, stderr=STDOUT,
                          close_fds=trac.util.compat.close_fds)
            except Exception as e:
                output += u'Error: hook execution failed with exception\n%s' % (traceback.format_exc(),)
                status = 500
            else:
                hookoutput = p.communicate(input=reqdata)[0]
                output += hookoutput.decode('utf-8')
                if p.returncode != 0:
                    output += u'Error: hook failed with exit code %d\n' % (p.returncode,)
                    status = 500

        for line in output.splitlines():
            self.log.debug(line)

        if status == 200 and not output:
            status = 204

        req.send(output.encode('utf-8'), 'text/plain', status)
예제 #39
0
파일: macro.py 프로젝트: nyuhuhuu/trachacks
 def _read_source_from_repos(self, formatter, src_path):
     repos_mgr = RepositoryManager(self.env)
     try: #0.12+
         repos_name, repos, source_obj = repos_mgr.get_repository_by_path(src_path)
     except AttributeError, e: #0.11
         repos = repos_mgr.get_repository(formatter.req.authname)
예제 #40
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 = int(req.args.get('limit') or self.default_log_limit)

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

        if not repos:
            raise ResourceNotFound(
                _("Repository '%(repo)s' not found", repo=reponame))

        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.
        revranges = None
        if revs:
            try:
                revranges = Ranges(revs)
                rev = revranges.b
            except ValueError:
                pass
        rev = unicode(repos.normalize_rev(rev))
        display_rev = repos.display_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('changeset')
        show_graph = False
        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:

            def history():
                prevpath = path
                expected_next_item = None
                ranges = list(revranges.pairs)
                ranges.reverse()
                for (a, b) in ranges:
                    a = repos.normalize_rev(a)
                    b = repos.normalize_rev(b)
                    while not repos.rev_older_than(b, a) and b != a:
                        node = get_existing_node(req, repos, prevpath, b)
                        node_history = list(node.get_history(2))
                        p, rev, chg = node_history[0]
                        if repos.rev_older_than(rev, a):
                            break  # simply skip, no separator
                        if 'CHANGESET_VIEW' in req.perm(cset_resource(id=rev)):
                            if expected_next_item:
                                # check whether we're continuing previous range
                                np, nrev, nchg = expected_next_item
                                if rev != nrev:  # no, we need a separator
                                    yield (np, nrev, None)
                            yield node_history[0]
                        prevpath = node_history[-1][0]  # follow copy
                        b = repos.previous_rev(rev)
                        if len(node_history) > 1:
                            expected_next_item = node_history[-1]
                        else:
                            expected_next_item = None
                if expected_next_item:
                    yield (expected_next_item[0], expected_next_item[1], 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
        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
        if 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=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 count >= limit:
            # stop_limit reached, there _might_ be some more
            next_rev = info[-1]['rev']
            next_path = info[-1]['path']
            next_revranges = None
            if revranges:
                next_revranges = str(revranges.truncate(next_rev))
            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=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':
            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, 'text/plain'
        elif format == 'rss':
            data['email_map'] = Chrome(self.env).get_email_map()
            data['context'] = web_context(req,
                                          'source',
                                          path,
                                          parent=repos.resource,
                                          absurls=True)
            return 'revisionlog.rss', data, '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, None
예제 #41
0
    def expand_macro(self, formatter, name, content):
        req = formatter.req
        args, kwargs = parse_args(content)
        args += [None, None]
        path, limit = args[:2]
        limit = kwargs.pop('limit', limit)
        package = kwargs.pop('package', None)

        if 'CHANGESET_VIEW' not in req.perm:
            return Markup('<i>Releases not available</i>')

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

        rev = repo.get_youngest_rev()
        rev = repo.normalize_rev(rev)
        path = repo.normalize_path(path)
        if limit is None:
            limit = 20
        else:
            limit = int(limit)

        releases = self.get_releases(repo, path, rev)

        # limit the releases after they have been sorted
        releases = releases[:1 + limit]
        items = []
        releases = [None] + releases + [None]

        # some extra checks to avoid using double-slashes
        if reponame == '':
            if path == '/':
                path = ''
            else:
                path = '/' + path
        elif path == '/':
            path = '/' + reponame.rstrip('/')
        else:
            path = '/' + reponame.rstrip('/') + '/' + path.lstrip('/')

        if not package:
            package = path.split("/")[-1]

        for i in xrange(len(releases) - 2):
            prev, cur, next = releases[i : i + 3]

            if prev == None and next == None:
                # no releases yet, just show trunk
                items.append(
                    " * "
                    " [/browser%(path)s/trunk trunk]"
                    " @[changeset:%(rev)s/%(reponame)s %(rev)s]"
                    " ("
                    "[/log%(path)s/trunk changes]"
                    " [/changeset?new_path=%(path)s/trunk diffs]"
                    ")"
                % {
                    'reponame' : reponame,
                    'path': path,
                    'rev': cur['rev'],
                })
            elif prev == None:
                # first entry = trunk
                items.append(
                    " * "
                    " [/browser%(path)s/trunk trunk]"
                    " @[changeset:%(rev)s/%(reponame)s %(rev)s]"
                    " ("
                    "[/log%(path)s/trunk?revs=%(stop_rev)s-%(rev)s changes]"
                    " [/changeset?old_path=%(path)s/tags/%(old_tag)s&new_path=%(path)s/trunk diffs]"
                    ")"
                % {
                    'reponame' : reponame,
                    'path': path,
                    'rev' : cur['rev'],
                    'old_tag' : next['version'],
                    'stop_rev' : next['rev'],
                })
            elif next != None:
                # regular releases
                release_page = 'release/%s-%s' % (package, cur['version'])
                page = WikiPage(self.env, release_page)
                if page.exists:
                    release_link = " [wiki:%s release notes]" % (release_page)
                else:
                    release_link = ""

                items.append(
                    " * '''%(date)s'''"
                    " [/log%(path)s/tags/%(new_tag)s %(new_tag)s] "
                    " @[changeset:%(rev)s/%(reponame)s %(rev)s]"
                    " by %(author)s"
                    " ("
                    "[/log%(path)s/trunk?revs=%(stop_rev)s-%(rev)s changes]"
                    " [/changeset?old_path=%(path)s/tags/%(old_tag)s&new_path=%(path)s/tags/%(new_tag)s diffs]"
                    "%(release_link)s"
                    ")"
                % {
                    'reponame' : reponame,
                    'path': path,
                    'date': cur['time'].strftime('%Y-%m-%d'),
                    'rev' : cur['rev'],
                    'stop_rev' : next['rev'],
                    'old_tag' : next['version'],
                    'new_tag' : cur['version'],
                    'author': cur['author'],
                    'release_link' : release_link,

                })
                url = self.specurl_annotate(cur);
                if url != None:
                    annotate = " spec: [%s annotate]" % url
                    items.append(annotate)
                    # check also diff link
                    url = self.specurl_diff(cur, next);
                    if url != None:
                        annotate = " [%s diff]" % url
                        items.append(annotate)
            else:
                # last release
                items.append(
                    " * '''%(date)s'''"
                    " [/log%(path)s/tags/%(new_tag)s?rev=%(rev)s&mode=follow_copy %(new_tag)s]"
                    " @[changeset:%(rev)s/%(reponame)s %(rev)s]"
                    " by %(author)s"
                % {
                    'reponame' : reponame,
                    'path': path,
                    'date': cur['time'].strftime('%Y-%m-%d'),
                    'rev' : cur['rev'],
                    'new_tag' : cur['version'],
                    'author': cur['author'],
                })

        return '<div class="releases">\n' + to_unicode(wiki_to_html("\n".join(items), self.env, req))  + '</div>\n'