Example #1
0
    def comment(self, repo_name, revision):
        comm = ChangesetCommentsModel().create(
            text=request.POST.get('text'),
            repo_id=c.rhodecode_db_repo.repo_id,
            user_id=c.rhodecode_user.user_id,
            revision=revision,
            f_path=request.POST.get('f_path'),
            line_no=request.POST.get('line'))
        Session.commit()
        if not request.environ.get('HTTP_X_PARTIAL_XHR'):
            return redirect(
                h.url('changeset_home', repo_name=repo_name,
                      revision=revision))

        data = {
            'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
        }
        if comm:
            c.co = comm
            data.update(comm.get_dict())
            data.update({
                'rendered_text':
                render('changeset/changeset_comment_block.html')
            })

        return data
Example #2
0
    def show(self, repo_name, pull_request_id):
        repo_model = RepoModel()
        c.users_array = repo_model.get_users_js()
        c.users_groups_array = repo_model.get_users_groups_js()
        c.pull_request = PullRequest.get_or_404(pull_request_id)
        c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request)
        cc_model = ChangesetCommentsModel()
        cs_model = ChangesetStatusModel()
        _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
                                            pull_request=c.pull_request,
                                            with_revisions=True)

        cs_statuses = defaultdict(list)
        for st in _cs_statuses:
            cs_statuses[st.author.username] += [st]

        c.pull_request_reviewers = []
        c.pull_request_pending_reviewers = []
        for o in c.pull_request.reviewers:
            st = cs_statuses.get(o.user.username, None)
            if st:
                sorter = lambda k: k.version
                st = [(x, list(y)[0])
                      for x, y in (groupby(sorted(st, key=sorter), sorter))]
            else:
                c.pull_request_pending_reviewers.append(o.user)
            c.pull_request_reviewers.append([o.user, st])

        # pull_requests repo_name we opened it against
        # ie. other_repo must match
        if repo_name != c.pull_request.other_repo.repo_name:
            raise HTTPNotFound

        # load compare data into template context
        enable_comments = not c.pull_request.is_closed()
        self._load_compare_data(c.pull_request, enable_comments=enable_comments)

        # inline comments
        c.inline_cnt = 0
        c.inline_comments = cc_model.get_inline_comments(
                                c.rhodecode_db_repo.repo_id,
                                pull_request=pull_request_id)
        # count inline comments
        for __, lines in c.inline_comments:
            for comments in lines.values():
                c.inline_cnt += len(comments)
        # comments
        c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
                                           pull_request=pull_request_id)

        # (badly named) pull-request status calculation based on reviewer votes
        c.current_changeset_status = cs_model.calculate_status(
                                        c.pull_request_reviewers,
                                         )
        c.changeset_statuses = ChangesetStatus.STATUSES

        c.as_form = False
        c.ancestor = None # there is one - but right here we don't know which
        return render('/pullrequests/pullrequest_show.html')
Example #3
0
    def show(self, repo_name, pull_request_id):
        repo_model = RepoModel()
        c.users_array = repo_model.get_users_js()
        c.users_groups_array = repo_model.get_users_groups_js()
        c.pull_request = PullRequest.get_or_404(pull_request_id)
        c.allowed_to_change_status = self._get_is_allowed_change_status(
            c.pull_request)
        cc_model = ChangesetCommentsModel()
        cs_model = ChangesetStatusModel()
        _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
                                             pull_request=c.pull_request,
                                             with_revisions=True)

        cs_statuses = defaultdict(list)
        for st in _cs_statuses:
            cs_statuses[st.author.username] += [st]

        c.pull_request_reviewers = []
        c.pull_request_pending_reviewers = []
        for o in c.pull_request.reviewers:
            st = cs_statuses.get(o.user.username, None)
            if st:
                sorter = lambda k: k.version
                st = [(x, list(y)[0])
                      for x, y in (groupby(sorted(st, key=sorter), sorter))]
            else:
                c.pull_request_pending_reviewers.append(o.user)
            c.pull_request_reviewers.append([o.user, st])

        # pull_requests repo_name we opened it against
        # ie. other_repo must match
        if repo_name != c.pull_request.other_repo.repo_name:
            raise HTTPNotFound

        # load compare data into template context
        enable_comments = not c.pull_request.is_closed()
        self._load_compare_data(c.pull_request,
                                enable_comments=enable_comments)

        # inline comments
        c.inline_cnt = 0
        c.inline_comments = cc_model.get_inline_comments(
            c.rhodecode_db_repo.repo_id, pull_request=pull_request_id)
        # count inline comments
        for __, lines in c.inline_comments:
            for comments in lines.values():
                c.inline_cnt += len(comments)
        # comments
        c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
                                           pull_request=pull_request_id)

        # (badly named) pull-request status calculation based on reviewer votes
        c.current_changeset_status = cs_model.calculate_status(
            c.pull_request_reviewers, )
        c.changeset_statuses = ChangesetStatus.STATUSES

        c.as_form = False
        c.ancestor = None  # there is one - but right here we don't know which
        return render('/pullrequests/pullrequest_show.html')
Example #4
0
    def comment(self, repo_name, pull_request_id):
        pull_request = PullRequest.get_or_404(pull_request_id)
        if pull_request.is_closed():
            raise HTTPForbidden()

        status = request.POST.get('changeset_status')
        change_status = request.POST.get('change_changeset_status')
        text = request.POST.get('text')
        if status and change_status:
            text = text or (_('Status change -> %s')
                            % ChangesetStatus.get_status_lbl(status))
        comm = ChangesetCommentsModel().create(
            text=text,
            repo=c.rhodecode_db_repo.repo_id,
            user=c.rhodecode_user.user_id,
            pull_request=pull_request_id,
            f_path=request.POST.get('f_path'),
            line_no=request.POST.get('line'),
            status_change=(ChangesetStatus.get_status_lbl(status)
                           if status and change_status else None)
        )

        # get status if set !
        if status and change_status:
            ChangesetStatusModel().set_status(
                c.rhodecode_db_repo.repo_id,
                status,
                c.rhodecode_user.user_id,
                comm,
                pull_request=pull_request_id
            )
        action_logger(self.rhodecode_user,
                      'user_commented_pull_request:%s' % pull_request_id,
                      c.rhodecode_db_repo, self.ip_addr, self.sa)

        if request.POST.get('save_close'):
            PullRequestModel().close_pull_request(pull_request_id)
            action_logger(self.rhodecode_user,
                      'user_closed_pull_request:%s' % pull_request_id,
                      c.rhodecode_db_repo, self.ip_addr, self.sa)

        Session().commit()

        if not request.environ.get('HTTP_X_PARTIAL_XHR'):
            return redirect(h.url('pullrequest_show', repo_name=repo_name,
                                  pull_request_id=pull_request_id))

        data = {
           'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
        }
        if comm:
            c.co = comm
            data.update(comm.get_dict())
            data.update({'rendered_text':
                         render('changeset/changeset_comment_block.html')})

        return data
def assert_inline_comments(pull_request, visible=None, outdated=None):
    if visible is not None:
        inline_comments = ChangesetCommentsModel().get_inline_comments(
            pull_request.target_repo.repo_id, pull_request=pull_request)
        assert len(inline_comments) == visible
    if outdated is not None:
        outdated_comments = ChangesetCommentsModel().get_outdated_comments(
            pull_request.target_repo.repo_id, pull_request)
        assert len(outdated_comments) == outdated
Example #6
0
    def comment(self, repo_name, revision):
        status = request.POST.get('changeset_status')
        change_status = request.POST.get('change_changeset_status')
        text = request.POST.get('text')
        if status and change_status:
            text = text or (_('Status change -> %s')
                            % ChangesetStatus.get_status_lbl(status))

        c.co = comm = ChangesetCommentsModel().create(
            text=text,
            repo=c.rhodecode_db_repo.repo_id,
            user=c.rhodecode_user.user_id,
            revision=revision,
            f_path=request.POST.get('f_path'),
            line_no=request.POST.get('line'),
            status_change=(ChangesetStatus.get_status_lbl(status)
                           if status and change_status else None)
        )

        # get status if set !
        if status and change_status:
            # if latest status was from pull request and it's closed
            # disallow changing status !
            # dont_allow_on_closed_pull_request = True !

            try:
                ChangesetStatusModel().set_status(
                    c.rhodecode_db_repo.repo_id,
                    status,
                    c.rhodecode_user.user_id,
                    comm,
                    revision=revision,
                    dont_allow_on_closed_pull_request=True
                )
            except StatusChangeOnClosedPullRequestError:
                log.error(traceback.format_exc())
                msg = _('Changing status on a changeset associated with '
                        'a closed pull request is not allowed')
                h.flash(msg, category='warning')
                return redirect(h.url('changeset_home', repo_name=repo_name,
                                      revision=revision))
        action_logger(self.rhodecode_user,
                      'user_commented_revision:%s' % revision,
                      c.rhodecode_db_repo, self.ip_addr, self.sa)

        Session().commit()

        if not request.environ.get('HTTP_X_PARTIAL_XHR'):
            return redirect(h.url('changeset_home', repo_name=repo_name,
                                  revision=revision))
        #only ajax below
        data = {
           'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
        }
        if comm:
            data.update(comm.get_dict())
            data.update({'rendered_text':
                         render('changeset/changeset_comment_block.html')})

        return data
    def close_pull_request_with_comment(self,
                                        pull_request,
                                        user,
                                        repo,
                                        message=None):
        status = ChangesetStatus.STATUS_REJECTED

        if not message:
            message = (_('Status change %(transition_icon)s %(status)s') % {
                'transition_icon': '>',
                'status': ChangesetStatus.get_status_lbl(status)
            })

        internal_message = _('Closing with') + ' ' + message

        comm = ChangesetCommentsModel().create(
            text=internal_message,
            repo=repo.repo_id,
            user=user.user_id,
            pull_request=pull_request.pull_request_id,
            f_path=None,
            line_no=None,
            status_change=ChangesetStatus.get_status_lbl(status),
            closing_pr=True)

        ChangesetStatusModel().set_status(
            repo.repo_id,
            status,
            user.user_id,
            comm,
            pull_request=pull_request.pull_request_id)
        Session().flush()

        PullRequestModel().close_pull_request(pull_request.pull_request_id,
                                              user)
    def test_api_comment_pull_request(self, pr_util, no_notifications):
        pull_request = pr_util.create_pull_request()
        pull_request_id = pull_request.pull_request_id
        author = pull_request.user_id
        repo = pull_request.target_repo.repo_id
        id_, params = build_data(self.apikey,
                                 'comment_pull_request',
                                 repoid=pull_request.target_repo.repo_name,
                                 pullrequestid=pull_request.pull_request_id,
                                 message='test message')
        response = api_call(self.app, params)
        pull_request = PullRequestModel().get(pull_request.pull_request_id)

        comments = ChangesetCommentsModel().get_comments(
            pull_request.target_repo.repo_id, pull_request=pull_request)

        expected = {
            'pull_request_id': pull_request.pull_request_id,
            'comment_id': comments[-1].comment_id,
            'status': None
        }
        assert_ok(id_, expected, response.body)

        action = 'user_commented_pull_request:%d' % pull_request_id
        journal = UserLog.query()\
            .filter(UserLog.user_id == author)\
            .filter(UserLog.repository_id == repo)\
            .filter(UserLog.action == action)\
            .all()
        assert len(journal) == 2
Example #9
0
 def delete_comment(self, repo_name, comment_id):
     co = ChangesetComment.get(comment_id)
     owner = lambda: co.author.user_id == c.rhodecode_user.user_id
     if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
         ChangesetCommentsModel().delete(comment=co)
         Session.commit()
         return True
     else:
         raise HTTPForbidden()
 def delete_comment(self, repo_name, comment_id):
     comment = ChangesetComment.get(comment_id)
     owner = (comment.author.user_id == c.rhodecode_user.user_id)
     is_repo_admin = h.HasRepoPermissionAny('repository.admin')(c.repo_name)
     if h.HasPermissionAny('hg.admin')() or is_repo_admin or owner:
         ChangesetCommentsModel().delete(comment=comment)
         Session().commit()
         return True
     else:
         raise HTTPForbidden()
Example #11
0
    def delete_comment(self, repo_name, comment_id):
        co = ChangesetComment.get(comment_id)
        if co.pull_request.is_closed():
            #don't allow deleting comments on closed pull request
            raise HTTPForbidden()

        owner = co.author.user_id == c.rhodecode_user.user_id
        if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
            ChangesetCommentsModel().delete(comment=co)
            Session().commit()
            return True
        else:
            raise HTTPForbidden()
    def _generate_update_diffs(self, pull_request, pull_request_version):
        diff_context = (self.DIFF_CONTEXT +
                        ChangesetCommentsModel.needed_extra_diff_context())
        old_diff = self._get_diff_from_pr_or_version(pull_request_version,
                                                     context=diff_context)
        new_diff = self._get_diff_from_pr_or_version(pull_request,
                                                     context=diff_context)

        old_diff_data = diffs.DiffProcessor(old_diff)
        old_diff_data.prepare()
        new_diff_data = diffs.DiffProcessor(new_diff)
        new_diff_data.prepare()

        return old_diff_data, new_diff_data
Example #13
0
    def comment(self, repo_name, revision):
        comm = ChangesetCommentsModel().create(
            text=request.POST.get('text'),
            repo_id=c.rhodecode_db_repo.repo_id,
            user_id=c.rhodecode_user.user_id,
            revision=revision,
            f_path=request.POST.get('f_path'),
            line_no=request.POST.get('line')
        )
        Session.commit()
        if not request.environ.get('HTTP_X_PARTIAL_XHR'):
            return redirect(h.url('changeset_home', repo_name=repo_name,
                                  revision=revision))

        data = {
           'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
        }
        if comm:
            c.co = comm
            data.update(comm.get_dict())
            data.update({'rendered_text':
                         render('changeset/changeset_comment_block.html')})

        return data
    def _comment_and_close_pr(self, pull_request, user, merge_state):
        pull_request.merge_rev = merge_state.merge_commit_id
        pull_request.updated_on = datetime.datetime.now()

        ChangesetCommentsModel().create(
            text=unicode(_('Pull request merged and closed')),
            repo=pull_request.target_repo.repo_id,
            user=user.user_id,
            pull_request=pull_request.pull_request_id,
            f_path=None,
            line_no=None,
            closing_pr=True)

        Session().add(pull_request)
        Session().flush()
        # TODO: paris: replace invalidation with less radical solution
        ScmModel().mark_for_invalidation(pull_request.target_repo.repo_name)
        self._trigger_pull_request_hook(pull_request, user, 'merge')
Example #15
0
    def _delete_comment(self, comment_id):
        comment_id = safe_int(comment_id)
        co = ChangesetComment.get_or_404(comment_id)
        if co.pull_request.is_closed():
            # don't allow deleting comments on closed pull request
            raise HTTPForbidden()

        is_owner = co.author.user_id == c.rhodecode_user.user_id
        is_repo_admin = h.HasRepoPermissionAny('repository.admin')(c.repo_name)
        if h.HasPermissionAny('hg.admin')() or is_repo_admin or is_owner:
            old_calculated_status = co.pull_request.calculated_review_status()
            ChangesetCommentsModel().delete(comment=co)
            Session().commit()
            calculated_status = co.pull_request.calculated_review_status()
            if old_calculated_status != calculated_status:
                PullRequestModel()._trigger_pull_request_hook(
                    co.pull_request, c.rhodecode_user, 'review_status_change')
            return True
        else:
            raise HTTPForbidden()
    def test_api_comment_pull_request_change_status(self, pr_util,
                                                    no_notifications):
        pull_request = pr_util.create_pull_request()
        pull_request_id = pull_request.pull_request_id
        id_, params = build_data(self.apikey,
                                 'comment_pull_request',
                                 repoid=pull_request.target_repo.repo_name,
                                 pullrequestid=pull_request.pull_request_id,
                                 status='rejected')
        response = api_call(self.app, params)
        pull_request = PullRequestModel().get(pull_request_id)

        comments = ChangesetCommentsModel().get_comments(
            pull_request.target_repo.repo_id, pull_request=pull_request)
        expected = {
            'pull_request_id': pull_request.pull_request_id,
            'comment_id': comments[-1].comment_id,
            'status': 'rejected'
        }
        assert_ok(id_, expected, response.body)
Example #17
0
    def show(self, repo_name, pull_request_id):
        pull_request_id = safe_int(pull_request_id)
        c.pull_request = PullRequest.get_or_404(pull_request_id)

        # pull_requests repo_name we opened it against
        # ie. target_repo must match
        if repo_name != c.pull_request.target_repo.repo_name:
            raise HTTPNotFound

        c.allowed_to_change_status = PullRequestModel(). \
            check_user_change_status(c.pull_request, c.rhodecode_user)
        c.allowed_to_update = PullRequestModel().check_user_update(
            c.pull_request,
            c.rhodecode_user) and not c.pull_request.is_closed()
        c.allowed_to_merge = PullRequestModel().check_user_merge(
            c.pull_request,
            c.rhodecode_user) and not c.pull_request.is_closed()

        cc_model = ChangesetCommentsModel()

        c.pull_request_reviewers = c.pull_request.reviewers_statuses()

        c.pull_request_review_status = c.pull_request.calculated_review_status(
        )
        c.pr_merge_status, c.pr_merge_msg = PullRequestModel().merge_status(
            c.pull_request)
        c.approval_msg = None
        if c.pull_request_review_status != ChangesetStatus.STATUS_APPROVED:
            c.approval_msg = _('Reviewer approval is pending.')
            c.pr_merge_status = False
        # load compare data into template context
        enable_comments = not c.pull_request.is_closed()
        self._load_compare_data(c.pull_request,
                                enable_comments=enable_comments)

        # this is a hack to properly display links, when creating PR, the
        # compare view and others uses different notation, and
        # compare_commits.html renders links based on the target_repo.
        # We need to swap that here to generate it properly on the html side
        c.target_repo = c.source_repo

        # inline comments
        c.inline_cnt = 0
        c.inline_comments = cc_model.get_inline_comments(
            c.rhodecode_db_repo.repo_id, pull_request=pull_request_id).items()
        # count inline comments
        for __, lines in c.inline_comments:
            for comments in lines.values():
                c.inline_cnt += len(comments)

        # outdated comments
        c.outdated_cnt = 0
        if ChangesetCommentsModel.use_outdated_comments(c.pull_request):
            c.outdated_comments = cc_model.get_outdated_comments(
                c.rhodecode_db_repo.repo_id, pull_request=c.pull_request)
            # Count outdated comments and check for deleted files
            for file_name, lines in c.outdated_comments.iteritems():
                for comments in lines.values():
                    c.outdated_cnt += len(comments)
                if file_name not in c.included_files:
                    c.deleted_files.append(file_name)
        else:
            c.outdated_comments = {}

        # comments
        c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
                                           pull_request=pull_request_id)

        if c.allowed_to_update:
            force_close = ('forced_closed', _('Close Pull Request'))
            statuses = ChangesetStatus.STATUSES + [force_close]
        else:
            statuses = ChangesetStatus.STATUSES
        c.commit_statuses = statuses

        c.ancestor = None  # TODO: add ancestor here

        return render('/pullrequests/pullrequest_show.html')
Example #18
0
    def comment(self, repo_name, pull_request_id):
        pull_request_id = safe_int(pull_request_id)
        pull_request = PullRequest.get_or_404(pull_request_id)
        if pull_request.is_closed():
            raise HTTPForbidden()

        # TODO: johbo: Re-think this bit, "approved_closed" does not exist
        # as a changeset status, still we want to send it in one value.
        status = request.POST.get('changeset_status', None)
        text = request.POST.get('text')
        if status and '_closed' in status:
            close_pr = True
            status = status.replace('_closed', '')
        else:
            close_pr = False

        forced = (status == 'forced')
        if forced:
            status = 'rejected'

        allowed_to_change_status = PullRequestModel().check_user_change_status(
            pull_request, c.rhodecode_user)

        if status and allowed_to_change_status:
            message = (_('Status change %(transition_icon)s %(status)s') % {
                'transition_icon': '>',
                'status': ChangesetStatus.get_status_lbl(status)
            })
            if close_pr:
                message = _('Closing with') + ' ' + message
            text = text or message
        comm = ChangesetCommentsModel().create(
            text=text,
            repo=c.rhodecode_db_repo.repo_id,
            user=c.rhodecode_user.user_id,
            pull_request=pull_request_id,
            f_path=request.POST.get('f_path'),
            line_no=request.POST.get('line'),
            status_change=(ChangesetStatus.get_status_lbl(status)
                           if status and allowed_to_change_status else None),
            closing_pr=close_pr)

        if allowed_to_change_status:
            old_calculated_status = pull_request.calculated_review_status()
            # get status if set !
            if status:
                ChangesetStatusModel().set_status(c.rhodecode_db_repo.repo_id,
                                                  status,
                                                  c.rhodecode_user.user_id,
                                                  comm,
                                                  pull_request=pull_request_id)

            Session().flush()
            # we now calculate the status of pull request, and based on that
            # calculation we set the commits status
            calculated_status = pull_request.calculated_review_status()
            if old_calculated_status != calculated_status:
                PullRequestModel()._trigger_pull_request_hook(
                    pull_request, c.rhodecode_user, 'review_status_change')

            calculated_status_lbl = ChangesetStatus.get_status_lbl(
                calculated_status)

            if close_pr:
                status_completed = (calculated_status in [
                    ChangesetStatus.STATUS_APPROVED,
                    ChangesetStatus.STATUS_REJECTED
                ])
                if forced or status_completed:
                    PullRequestModel().close_pull_request(
                        pull_request_id, c.rhodecode_user)
                else:
                    h.flash(_('Closing pull request on other statuses than '
                              'rejected or approved is forbidden. '
                              'Calculated status from all reviewers '
                              'is currently: %s') % calculated_status_lbl,
                            category='warning')

        Session().commit()

        if not request.is_xhr:
            return redirect(
                h.url('pullrequest_show',
                      repo_name=repo_name,
                      pull_request_id=pull_request_id))

        data = {
            'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
        }
        if comm:
            c.co = comm
            data.update(comm.get_dict())
            data.update({
                'rendered_text':
                render('changeset/changeset_comment_block.html')
            })

        return data
Example #19
0
class ChangesetController(BaseRepoController):
    @LoginRequired()
    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
                                   'repository.admin')
    def __before__(self):
        super(ChangesetController, self).__before__()
        c.affected_files_cut_off = 60

    def index(self, revision):

        c.anchor_url = anchor_url
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url
        limit_off = request.GET.get('fulldiff')
        #get ranges of revisions if preset
        rev_range = revision.split('...')[:2]
        enable_comments = True
        try:
            if len(rev_range) == 2:
                enable_comments = False
                rev_start = rev_range[0]
                rev_end = rev_range[1]
                rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
                                                             end=rev_end)
            else:
                rev_ranges = [c.rhodecode_repo.get_changeset(revision)]

            c.cs_ranges = list(rev_ranges)
            if not c.cs_ranges:
                raise RepositoryError('Changeset range returned empty result')

        except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
            log.error(traceback.format_exc())
            h.flash(str(e), category='warning')
            return redirect(url('home'))

        c.changes = OrderedDict()

        c.lines_added = 0  # count of lines added
        c.lines_deleted = 0  # count of lines removes

        cumulative_diff = 0
        c.cut_off = False  # defines if cut off limit is reached

        c.comments = []
        c.inline_comments = []
        c.inline_cnt = 0
        # Iterate over ranges (default changeset view is always one changeset)
        for changeset in c.cs_ranges:
            c.comments.extend(ChangesetCommentsModel()\
                              .get_comments(c.rhodecode_db_repo.repo_id,
                                            changeset.raw_id))
            inlines = ChangesetCommentsModel()\
                        .get_inline_comments(c.rhodecode_db_repo.repo_id,
                                             changeset.raw_id)
            c.inline_comments.extend(inlines)
            c.changes[changeset.raw_id] = []
            try:
                changeset_parent = changeset.parents[0]
            except IndexError:
                changeset_parent = None

            #==================================================================
            # ADDED FILES
            #==================================================================
            for node in changeset.added:
                fid = h.FID(revision, node.path)
                line_context_lcl = get_line_ctx(fid, request.GET)
                ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
                lim = self.cut_off_limit
                if cumulative_diff > self.cut_off_limit:
                    lim = -1 if limit_off is None else None
                size, cs1, cs2, diff, st = wrapped_diff(
                    filenode_old=None,
                    filenode_new=node,
                    cut_off_limit=lim,
                    ignore_whitespace=ign_whitespace_lcl,
                    line_context=line_context_lcl,
                    enable_comments=enable_comments)
                cumulative_diff += size
                c.lines_added += st[0]
                c.lines_deleted += st[1]
                c.changes[changeset.raw_id].append(
                    ('added', node, diff, cs1, cs2, st))

            #==================================================================
            # CHANGED FILES
            #==================================================================
            for node in changeset.changed:
                try:
                    filenode_old = changeset_parent.get_node(node.path)
                except ChangesetError:
                    log.warning('Unable to fetch parent node for diff')
                    filenode_old = FileNode(node.path, '', EmptyChangeset())

                fid = h.FID(revision, node.path)
                line_context_lcl = get_line_ctx(fid, request.GET)
                ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
                lim = self.cut_off_limit
                if cumulative_diff > self.cut_off_limit:
                    lim = -1 if limit_off is None else None
                size, cs1, cs2, diff, st = wrapped_diff(
                    filenode_old=filenode_old,
                    filenode_new=node,
                    cut_off_limit=lim,
                    ignore_whitespace=ign_whitespace_lcl,
                    line_context=line_context_lcl,
                    enable_comments=enable_comments)
                cumulative_diff += size
                c.lines_added += st[0]
                c.lines_deleted += st[1]
                c.changes[changeset.raw_id].append(
                    ('changed', node, diff, cs1, cs2, st))
            #==================================================================
            # REMOVED FILES
            #==================================================================
            for node in changeset.removed:
                c.changes[changeset.raw_id].append(
                    ('removed', node, None, None, None, (0, 0)))

        # count inline comments
        for path, lines in c.inline_comments:
            for comments in lines.values():
                c.inline_cnt += len(comments)

        if len(c.cs_ranges) == 1:
            c.changeset = c.cs_ranges[0]
            c.changes = c.changes[c.changeset.raw_id]

            return render('changeset/changeset.html')
        else:
            return render('changeset/changeset_range.html')
Example #20
0
    def show(self, repo_name, pull_request_id):
        repo_model = RepoModel()
        c.users_array = repo_model.get_users_js()
        c.users_groups_array = repo_model.get_users_groups_js()
        c.pull_request = PullRequest.get_or_404(pull_request_id)
        c.target_repo = c.pull_request.org_repo.repo_name

        cc_model = ChangesetCommentsModel()
        cs_model = ChangesetStatusModel()
        _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
                                            pull_request=c.pull_request,
                                            with_revisions=True)

        cs_statuses = defaultdict(list)
        for st in _cs_statuses:
            cs_statuses[st.author.username] += [st]

        c.pull_request_reviewers = []
        c.pull_request_pending_reviewers = []
        for o in c.pull_request.reviewers:
            st = cs_statuses.get(o.user.username, None)
            if st:
                sorter = lambda k: k.version
                st = [(x, list(y)[0])
                      for x, y in (groupby(sorted(st, key=sorter), sorter))]
            else:
                c.pull_request_pending_reviewers.append(o.user)
            c.pull_request_reviewers.append([o.user, st])

        # pull_requests repo_name we opened it against
        # ie. other_repo must match
        if repo_name != c.pull_request.other_repo.repo_name:
            raise HTTPNotFound

        # load compare data into template context
        enable_comments = not c.pull_request.is_closed()
        self._load_compare_data(c.pull_request, enable_comments=enable_comments)

        # inline comments
        c.inline_cnt = 0
        c.inline_comments = cc_model.get_inline_comments(
                                c.rhodecode_db_repo.repo_id,
                                pull_request=pull_request_id)
        # count inline comments
        for __, lines in c.inline_comments:
            for comments in lines.values():
                c.inline_cnt += len(comments)
        # comments
        c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
                                           pull_request=pull_request_id)

        try:
            cur_status = c.statuses[c.pull_request.revisions[0]][0]
        except:
            log.error(traceback.format_exc())
            cur_status = 'undefined'
        if c.pull_request.is_closed() and 0:
            c.current_changeset_status = cur_status
        else:
            # changeset(pull-request) status calulation based on reviewers
            c.current_changeset_status = cs_model.calculate_status(
                                            c.pull_request_reviewers,
                                         )
        c.changeset_statuses = ChangesetStatus.STATUSES

        return render('/pullrequests/pullrequest_show.html')
def comment_pull_request(request,
                         apiuser,
                         repoid,
                         pullrequestid,
                         message=Optional(None),
                         status=Optional(None),
                         userid=Optional(OAttr('apiuser'))):
    """
    Comment on the pull request specified with the `pullrequestid`,
    in the |repo| specified by the `repoid`, and optionally change the
    review status.

    :param apiuser: This is filled automatically from the |authtoken|.
    :type apiuser: AuthUser
    :param repoid: The repository name or repository ID.
    :type repoid: str or int
    :param pullrequestid: The pull request ID.
    :type pullrequestid: int
    :param message: The text content of the comment.
    :type message: str
    :param status: (**Optional**) Set the approval status of the pull
        request. Valid options are:
        * not_reviewed
        * approved
        * rejected
        * under_review
    :type status: str
    :param userid: Comment on the pull request as this user
    :type userid: Optional(str or int)

    Example output:

    .. code-block:: bash

      id : <id_given_in_input>
      result :
        {
            "pull_request_id":  "<Integer>",
            "comment_id":       "<Integer>"
        }
      error :  null
    """
    repo = get_repo_or_error(repoid)
    if not isinstance(userid, Optional):
        if (has_superadmin_permission(apiuser)
                or HasRepoPermissionAnyApi('repository.admin')(
                    user=apiuser, repo_name=repo.repo_name)):
            apiuser = get_user_or_error(userid)
        else:
            raise JSONRPCError('userid is not the same as your user')

    pull_request = get_pull_request_or_error(pullrequestid)
    if not PullRequestModel().check_user_read(pull_request, apiuser, api=True):
        raise JSONRPCError('repository `%s` does not exist' % (repoid, ))
    message = Optional.extract(message)
    status = Optional.extract(status)
    if not message and not status:
        raise JSONRPCError('message and status parameter missing')

    if (status not in (st[0] for st in ChangesetStatus.STATUSES)
            and status is not None):
        raise JSONRPCError('unknown comment status`%s`' % status)

    allowed_to_change_status = PullRequestModel().check_user_change_status(
        pull_request, apiuser)
    text = message
    if status and allowed_to_change_status:
        st_message = (('Status change %(transition_icon)s %(status)s') % {
            'transition_icon': '>',
            'status': ChangesetStatus.get_status_lbl(status)
        })
        text = message or st_message

    rc_config = SettingsModel().get_all_settings()
    renderer = rc_config.get('rhodecode_markup_renderer', 'rst')
    comment = ChangesetCommentsModel().create(
        text=text,
        repo=pull_request.target_repo.repo_id,
        user=apiuser.user_id,
        pull_request=pull_request.pull_request_id,
        f_path=None,
        line_no=None,
        status_change=(ChangesetStatus.get_status_lbl(status)
                       if status and allowed_to_change_status else None),
        closing_pr=False,
        renderer=renderer)

    if allowed_to_change_status and status:
        ChangesetStatusModel().set_status(
            pull_request.target_repo.repo_id,
            status,
            apiuser.user_id,
            comment,
            pull_request=pull_request.pull_request_id)
        Session().flush()

    Session().commit()
    data = {
        'pull_request_id': pull_request.pull_request_id,
        'comment_id': comment.comment_id,
        'status': status
    }
    return data
Example #22
0
    def comment(self, repo_name, pull_request_id):
        pull_request = PullRequest.get_or_404(pull_request_id)
        if pull_request.is_closed():
            raise HTTPForbidden()

        status = request.POST.get('changeset_status')
        change_status = request.POST.get('change_changeset_status')
        text = request.POST.get('text')
        close_pr = request.POST.get('save_close')

        allowed_to_change_status = self._get_is_allowed_change_status(pull_request)
        if status and change_status and allowed_to_change_status:
            _def = (_('Status change -> %s')
                            % ChangesetStatus.get_status_lbl(status))
            if close_pr:
                _def = _('Closing with') + ' ' + _def
            text = text or _def
        comm = ChangesetCommentsModel().create(
            text=text,
            repo=c.rhodecode_db_repo.repo_id,
            user=c.rhodecode_user.user_id,
            pull_request=pull_request_id,
            f_path=request.POST.get('f_path'),
            line_no=request.POST.get('line'),
            status_change=(ChangesetStatus.get_status_lbl(status)
                           if status and change_status
                           and allowed_to_change_status else None),
            closing_pr=close_pr
        )

        action_logger(self.rhodecode_user,
                      'user_commented_pull_request:%s' % pull_request_id,
                      c.rhodecode_db_repo, self.ip_addr, self.sa)

        if allowed_to_change_status:
            # get status if set !
            if status and change_status:
                ChangesetStatusModel().set_status(
                    c.rhodecode_db_repo.repo_id,
                    status,
                    c.rhodecode_user.user_id,
                    comm,
                    pull_request=pull_request_id
                )

            if close_pr:
                if status in ['rejected', 'approved']:
                    PullRequestModel().close_pull_request(pull_request_id)
                    action_logger(self.rhodecode_user,
                              'user_closed_pull_request:%s' % pull_request_id,
                              c.rhodecode_db_repo, self.ip_addr, self.sa)
                else:
                    h.flash(_('Closing pull request on other statuses than '
                              'rejected or approved forbidden'),
                            category='warning')

        Session().commit()

        if not request.environ.get('HTTP_X_PARTIAL_XHR'):
            return redirect(h.url('pullrequest_show', repo_name=repo_name,
                                  pull_request_id=pull_request_id))

        data = {
           'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
        }
        if comm:
            c.co = comm
            data.update(comm.get_dict())
            data.update({'rendered_text':
                         render('changeset/changeset_comment_block.html')})

        return data
    def _index(self, commit_id_range, method):
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url
        c.fulldiff = fulldiff = request.GET.get('fulldiff')
        # get ranges of commit ids if preset
        commit_range = commit_id_range.split('...')[:2]
        enable_comments = True
        try:
            pre_load = [
                'affected_files', 'author', 'branch', 'date', 'message',
                'parents'
            ]

            if len(commit_range) == 2:
                enable_comments = False
                commits = c.rhodecode_repo.get_commits(
                    start_id=commit_range[0],
                    end_id=commit_range[1],
                    pre_load=pre_load)
                commits = list(commits)
            else:
                commits = [
                    c.rhodecode_repo.get_commit(commit_id=commit_id_range,
                                                pre_load=pre_load)
                ]

            c.commit_ranges = commits
            if not c.commit_ranges:
                raise RepositoryError(
                    'The commit range returned an empty result')
        except CommitDoesNotExistError:
            msg = _('No such commit exists for this repository')
            h.flash(msg, category='error')
            raise HTTPNotFound()
        except Exception:
            log.exception("General failure")
            raise HTTPNotFound()

        c.changes = OrderedDict()
        c.lines_added = 0
        c.lines_deleted = 0

        c.commit_statuses = ChangesetStatus.STATUSES
        c.comments = []
        c.statuses = []
        c.inline_comments = []
        c.inline_cnt = 0
        c.files = []

        # Iterate over ranges (default commit view is always one commit)
        for commit in c.commit_ranges:
            if method == 'show':
                c.statuses.extend([
                    ChangesetStatusModel().get_status(
                        c.rhodecode_db_repo.repo_id, commit.raw_id)
                ])

                c.comments.extend(ChangesetCommentsModel().get_comments(
                    c.rhodecode_db_repo.repo_id, revision=commit.raw_id))

                # comments from PR
                st = ChangesetStatusModel().get_statuses(
                    c.rhodecode_db_repo.repo_id,
                    commit.raw_id,
                    with_revisions=True)

                # from associated statuses, check the pull requests, and
                # show comments from them

                prs = set(
                    x.pull_request
                    for x in filter(lambda x: x.pull_request is not None, st))
                for pr in prs:
                    c.comments.extend(pr.comments)

                inlines = ChangesetCommentsModel().get_inline_comments(
                    c.rhodecode_db_repo.repo_id, revision=commit.raw_id)
                c.inline_comments.extend(inlines.iteritems())

            c.changes[commit.raw_id] = []

            commit2 = commit
            commit1 = commit.parents[0] if commit.parents else EmptyCommit()

            # fetch global flags of ignore ws or context lines
            context_lcl = get_line_ctx('', request.GET)
            ign_whitespace_lcl = get_ignore_ws('', request.GET)

            _diff = c.rhodecode_repo.get_diff(
                commit1,
                commit2,
                ignore_whitespace=ign_whitespace_lcl,
                context=context_lcl)

            # diff_limit will cut off the whole diff if the limit is applied
            # otherwise it will just hide the big files from the front-end
            diff_limit = self.cut_off_limit_diff
            file_limit = self.cut_off_limit_file

            diff_processor = diffs.DiffProcessor(_diff,
                                                 format='gitdiff',
                                                 diff_limit=diff_limit,
                                                 file_limit=file_limit,
                                                 show_full_diff=fulldiff)
            commit_changes = OrderedDict()
            if method == 'show':
                _parsed = diff_processor.prepare()
                c.limited_diff = isinstance(_parsed,
                                            diffs.LimitedDiffContainer)
                for f in _parsed:
                    c.files.append(f)
                    st = f['stats']
                    c.lines_added += st['added']
                    c.lines_deleted += st['deleted']
                    fid = h.FID(commit.raw_id, f['filename'])
                    diff = diff_processor.as_html(
                        enable_comments=enable_comments, parsed_lines=[f])
                    commit_changes[fid] = [
                        commit1.raw_id, commit2.raw_id, f['operation'],
                        f['filename'], diff, st, f
                    ]
            else:
                # downloads/raw we only need RAW diff nothing else
                diff = diff_processor.as_raw()
                commit_changes[''] = [None, None, None, None, diff, None, None]
            c.changes[commit.raw_id] = commit_changes

        # sort comments by how they were generated
        c.comments = sorted(c.comments, key=lambda x: x.comment_id)

        # count inline comments
        for __, lines in c.inline_comments:
            for comments in lines.values():
                c.inline_cnt += len(comments)

        if len(c.commit_ranges) == 1:
            c.commit = c.commit_ranges[0]
            c.parent_tmpl = ''.join('# Parent  %s\n' % x.raw_id
                                    for x in c.commit.parents)
        if method == 'download':
            response.content_type = 'text/plain'
            response.content_disposition = ('attachment; filename=%s.diff' %
                                            commit_id_range[:12])
            return diff
        elif method == 'patch':
            response.content_type = 'text/plain'
            c.diff = safe_unicode(diff)
            return render('changeset/patch_changeset.html')
        elif method == 'raw':
            response.content_type = 'text/plain'
            return diff
        elif method == 'show':
            if len(c.commit_ranges) == 1:
                return render('changeset/changeset.html')
            else:
                c.ancestor = None
                c.target_repo = c.rhodecode_db_repo
                return render('changeset/changeset_range.html')
Example #24
0
    def comment(self, repo_name, revision):
        status = request.POST.get('changeset_status')
        change_status = request.POST.get('change_changeset_status')
        text = request.POST.get('text')
        if status and change_status:
            text = text or (_('Status change -> %s')
                            % ChangesetStatus.get_status_lbl(status))

        comm = ChangesetCommentsModel().create(
            text=text,
            repo=c.rhodecode_db_repo.repo_id,
            user=c.rhodecode_user.user_id,
            revision=revision,
            f_path=request.POST.get('f_path'),
            line_no=request.POST.get('line'),
            status_change=(ChangesetStatus.get_status_lbl(status)
                           if status and change_status else None)
        )

        # get status if set !
        if status and change_status:
            # if latest status was from pull request and it's closed
            # disallow changing status !
            # dont_allow_on_closed_pull_request = True !

            try:
                ChangesetStatusModel().set_status(
                    c.rhodecode_db_repo.repo_id,
                    status,
                    c.rhodecode_user.user_id,
                    comm,
                    revision=revision,
                    dont_allow_on_closed_pull_request=True
                )
            except StatusChangeOnClosedPullRequestError:
                log.error(traceback.format_exc())
                msg = _('Changing status on a changeset associated with'
                        'a closed pull request is not allowed')
                h.flash(msg, category='warning')
                return redirect(h.url('changeset_home', repo_name=repo_name,
                                      revision=revision))
        action_logger(self.rhodecode_user,
                      'user_commented_revision:%s' % revision,
                      c.rhodecode_db_repo, self.ip_addr, self.sa)

        Session().commit()

        if not request.environ.get('HTTP_X_PARTIAL_XHR'):
            return redirect(h.url('changeset_home', repo_name=repo_name,
                                  revision=revision))

        data = {
           'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
        }
        if comm:
            c.co = comm
            data.update(comm.get_dict())
            data.update({'rendered_text':
                         render('changeset/changeset_comment_block.html')})

        return data
    def comment(self, repo_name, revision):
        commit_id = revision
        status = request.POST.get('changeset_status', None)
        text = request.POST.get('text')
        if status:
            text = text or (
                _('Status change %(transition_icon)s %(status)s') % {
                    'transition_icon': '>',
                    'status': ChangesetStatus.get_status_lbl(status)
                })

        multi_commit_ids = filter(
            lambda s: s not in ['', None],
            request.POST.get('commit_ids', '').split(','),
        )

        commit_ids = multi_commit_ids or [commit_id]
        comment = None
        for current_id in filter(None, commit_ids):
            c.co = comment = ChangesetCommentsModel().create(
                text=text,
                repo=c.rhodecode_db_repo.repo_id,
                user=c.rhodecode_user.user_id,
                revision=current_id,
                f_path=request.POST.get('f_path'),
                line_no=request.POST.get('line'),
                status_change=(ChangesetStatus.get_status_lbl(status)
                               if status else None))
            # get status if set !
            if status:
                # if latest status was from pull request and it's closed
                # disallow changing status !
                # dont_allow_on_closed_pull_request = True !

                try:
                    ChangesetStatusModel().set_status(
                        c.rhodecode_db_repo.repo_id,
                        status,
                        c.rhodecode_user.user_id,
                        comment,
                        revision=current_id,
                        dont_allow_on_closed_pull_request=True)
                except StatusChangeOnClosedPullRequestError:
                    msg = _('Changing the status of a commit associated with '
                            'a closed pull request is not allowed')
                    log.exception(msg)
                    h.flash(msg, category='warning')
                    return redirect(
                        h.url('changeset_home',
                              repo_name=repo_name,
                              revision=current_id))

        # finalize, commit and redirect
        Session().commit()

        data = {
            'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
        }
        if comment:
            data.update(comment.get_dict())
            data.update({
                'rendered_text':
                render('changeset/changeset_comment_block.html')
            })

        return data
Example #26
0
    def comment(self, repo_name, pull_request_id):
        pull_request = PullRequest.get_or_404(pull_request_id)
        if pull_request.is_closed():
            raise HTTPForbidden()

        status = request.POST.get('changeset_status')
        change_status = request.POST.get('change_changeset_status')
        text = request.POST.get('text')
        close_pr = request.POST.get('save_close')

        allowed_to_change_status = self._get_is_allowed_change_status(
            pull_request)
        if status and change_status and allowed_to_change_status:
            _def = (_('Status change -> %s') %
                    ChangesetStatus.get_status_lbl(status))
            if close_pr:
                _def = _('Closing with') + ' ' + _def
            text = text or _def
        comm = ChangesetCommentsModel().create(
            text=text,
            repo=c.rhodecode_db_repo.repo_id,
            user=c.rhodecode_user.user_id,
            pull_request=pull_request_id,
            f_path=request.POST.get('f_path'),
            line_no=request.POST.get('line'),
            status_change=(ChangesetStatus.get_status_lbl(status)
                           if status and change_status
                           and allowed_to_change_status else None),
            closing_pr=close_pr)

        action_logger(self.rhodecode_user,
                      'user_commented_pull_request:%s' % pull_request_id,
                      c.rhodecode_db_repo, self.ip_addr, self.sa)

        if allowed_to_change_status:
            # get status if set !
            if status and change_status:
                ChangesetStatusModel().set_status(c.rhodecode_db_repo.repo_id,
                                                  status,
                                                  c.rhodecode_user.user_id,
                                                  comm,
                                                  pull_request=pull_request_id)

            if close_pr:
                if status in ['rejected', 'approved']:
                    PullRequestModel().close_pull_request(pull_request_id)
                    action_logger(
                        self.rhodecode_user,
                        'user_closed_pull_request:%s' % pull_request_id,
                        c.rhodecode_db_repo, self.ip_addr, self.sa)
                else:
                    h.flash(_('Closing pull request on other statuses than '
                              'rejected or approved forbidden'),
                            category='warning')

        Session().commit()

        if not request.environ.get('HTTP_X_PARTIAL_XHR'):
            return redirect(
                h.url('pullrequest_show',
                      repo_name=repo_name,
                      pull_request_id=pull_request_id))

        data = {
            'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
        }
        if comm:
            c.co = comm
            data.update(comm.get_dict())
            data.update({
                'rendered_text':
                render('changeset/changeset_comment_block.html')
            })

        return data
    def update_commits(self, pull_request):
        """
        Get the updated list of commits for the pull request
        and return the new pull request version and the list
        of commits processed by this update action
        """

        pull_request = self.__get_pull_request(pull_request)
        source_ref_type = pull_request.source_ref_parts.type
        source_ref_name = pull_request.source_ref_parts.name
        source_ref_id = pull_request.source_ref_parts.commit_id

        if not self.has_valid_update_type(pull_request):
            log.debug("Skipping update of pull request %s due to ref type: %s",
                      pull_request, source_ref_type)
            return (None, None)

        source_repo = pull_request.source_repo.scm_instance()
        source_commit = source_repo.get_commit(commit_id=source_ref_name)
        if source_ref_id == source_commit.raw_id:
            log.debug("Nothing changed in pull request %s", pull_request)
            return (None, None)

        # Finally there is a need for an update
        pull_request_version = self._create_version_from_snapshot(pull_request)
        self._link_comments_to_version(pull_request_version)

        target_ref_type = pull_request.target_ref_parts.type
        target_ref_name = pull_request.target_ref_parts.name
        target_ref_id = pull_request.target_ref_parts.commit_id
        target_repo = pull_request.target_repo.scm_instance()

        if target_ref_type in ('tag', 'branch', 'book'):
            target_commit = target_repo.get_commit(target_ref_name)
        else:
            target_commit = target_repo.get_commit(target_ref_id)

        # re-compute commit ids
        old_commit_ids = set(pull_request.revisions)
        pre_load = ["author", "branch", "date", "message"]
        commit_ranges = target_repo.compare(target_commit.raw_id,
                                            source_commit.raw_id,
                                            source_repo,
                                            merge=True,
                                            pre_load=pre_load)

        ancestor = target_repo.get_common_ancestor(target_commit.raw_id,
                                                   source_commit.raw_id,
                                                   source_repo)

        pull_request.source_ref = '%s:%s:%s' % (
            source_ref_type, source_ref_name, source_commit.raw_id)
        pull_request.target_ref = '%s:%s:%s' % (target_ref_type,
                                                target_ref_name, ancestor)
        pull_request.revisions = [
            commit.raw_id for commit in reversed(commit_ranges)
        ]
        pull_request.updated_on = datetime.datetime.now()
        Session().add(pull_request)
        new_commit_ids = set(pull_request.revisions)

        changes = self._calculate_commit_id_changes(old_commit_ids,
                                                    new_commit_ids)

        old_diff_data, new_diff_data = self._generate_update_diffs(
            pull_request, pull_request_version)

        ChangesetCommentsModel().outdate_comments(pull_request,
                                                  old_diff_data=old_diff_data,
                                                  new_diff_data=new_diff_data)

        file_changes = self._calculate_file_changes(old_diff_data,
                                                    new_diff_data)

        # Add an automatic comment to the pull request
        update_comment = ChangesetCommentsModel().create(
            text=self._render_update_message(changes, file_changes),
            repo=pull_request.target_repo,
            user=pull_request.author,
            pull_request=pull_request,
            send_email=False,
            renderer=DEFAULT_COMMENTS_RENDERER)

        # Update status to "Under Review" for added commits
        for commit_id in changes.added:
            ChangesetStatusModel().set_status(
                repo=pull_request.source_repo,
                status=ChangesetStatus.STATUS_UNDER_REVIEW,
                comment=update_comment,
                user=pull_request.author,
                pull_request=pull_request,
                revision=commit_id)

        log.debug(
            'Updated pull request %s, added_ids: %s, common_ids: %s, '
            'removed_ids: %s', pull_request.pull_request_id, changes.added,
            changes.common, changes.removed)
        log.debug('Updated pull request with the following file changes: %s',
                  file_changes)

        log.info(
            "Updated pull request %s from commit %s to commit %s, "
            "stored new version %s of this pull request.",
            pull_request.pull_request_id, source_ref_id,
            pull_request.source_ref_parts.commit_id,
            pull_request_version.pull_request_version_id)
        Session().commit()
        self._trigger_pull_request_hook(pull_request, pull_request.author,
                                        'update')
        return (pull_request_version, changes)
Example #28
0
    def _get_pull_requests_list(self, repo_name, opened_by, statuses):
        # pagination
        start = safe_int(request.GET.get('start'), 0)
        length = safe_int(request.GET.get('length'), c.visual.dashboard_items)
        order_by, order_dir = self._extract_ordering(request)

        if c.awaiting_review:
            pull_requests = PullRequestModel().get_awaiting_review(
                repo_name,
                source=c.source,
                opened_by=opened_by,
                statuses=statuses,
                offset=start,
                length=length,
                order_by=order_by,
                order_dir=order_dir)
            pull_requests_total_count = PullRequestModel(
            ).count_awaiting_review(repo_name,
                                    source=c.source,
                                    statuses=statuses,
                                    opened_by=opened_by)
        elif c.awaiting_my_review:
            pull_requests = PullRequestModel().get_awaiting_my_review(
                repo_name,
                source=c.source,
                opened_by=opened_by,
                user_id=c.rhodecode_user.user_id,
                statuses=statuses,
                offset=start,
                length=length,
                order_by=order_by,
                order_dir=order_dir)
            pull_requests_total_count = PullRequestModel(
            ).count_awaiting_my_review(repo_name,
                                       source=c.source,
                                       user_id=c.rhodecode_user.user_id,
                                       statuses=statuses,
                                       opened_by=opened_by)
        else:
            pull_requests = PullRequestModel().get_all(repo_name,
                                                       source=c.source,
                                                       opened_by=opened_by,
                                                       statuses=statuses,
                                                       offset=start,
                                                       length=length,
                                                       order_by=order_by,
                                                       order_dir=order_dir)
            pull_requests_total_count = PullRequestModel().count_all(
                repo_name,
                source=c.source,
                statuses=statuses,
                opened_by=opened_by)

        from rhodecode.lib.utils import PartialRenderer
        _render = PartialRenderer('data_table/_dt_elements.html')
        data = []
        for pr in pull_requests:
            comments = ChangesetCommentsModel().get_all_comments(
                c.rhodecode_db_repo.repo_id, pull_request=pr)

            data.append({
                'name':
                _render('pullrequest_name', pr.pull_request_id,
                        pr.target_repo.repo_name),
                'name_raw':
                pr.pull_request_id,
                'status':
                _render('pullrequest_status', pr.calculated_review_status()),
                'title':
                _render('pullrequest_title', pr.title, pr.description),
                'description':
                h.escape(pr.description),
                'updated_on':
                _render('pullrequest_updated_on',
                        h.datetime_to_time(pr.updated_on)),
                'updated_on_raw':
                h.datetime_to_time(pr.updated_on),
                'created_on':
                _render('pullrequest_updated_on',
                        h.datetime_to_time(pr.created_on)),
                'created_on_raw':
                h.datetime_to_time(pr.created_on),
                'author':
                _render(
                    'pullrequest_author',
                    pr.author.full_contact,
                ),
                'author_raw':
                pr.author.full_name,
                'comments':
                _render('pullrequest_comments', len(comments)),
                'comments_raw':
                len(comments),
                'closed':
                pr.is_closed(),
            })
        # json used to render the grid
        data = ({
            'data': data,
            'recordsTotal': pull_requests_total_count,
            'recordsFiltered': pull_requests_total_count,
        })
        return data
Example #29
0
class ChangesetController(BaseRepoController):

    @LoginRequired()
    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
                                   'repository.admin')
    def __before__(self):
        super(ChangesetController, self).__before__()
        c.affected_files_cut_off = 60
        repo_model = RepoModel()
        c.users_array = repo_model.get_users_js()
        c.users_groups_array = repo_model.get_users_groups_js()

    def index(self, revision, method='show'):
        c.anchor_url = anchor_url
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url
        c.fulldiff = fulldiff = request.GET.get('fulldiff')
        #get ranges of revisions if preset
        rev_range = revision.split('...')[:2]
        enable_comments = True
        try:
            if len(rev_range) == 2:
                enable_comments = False
                rev_start = rev_range[0]
                rev_end = rev_range[1]
                rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
                                                             end=rev_end)
            else:
                rev_ranges = [c.rhodecode_repo.get_changeset(revision)]

            c.cs_ranges = list(rev_ranges)
            if not c.cs_ranges:
                raise RepositoryError('Changeset range returned empty result')

        except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
            log.error(traceback.format_exc())
            h.flash(str(e), category='error')
            raise HTTPNotFound()

        c.changes = OrderedDict()

        c.lines_added = 0  # count of lines added
        c.lines_deleted = 0  # count of lines removes

        c.changeset_statuses = ChangesetStatus.STATUSES
        c.comments = []
        c.statuses = []
        c.inline_comments = []
        c.inline_cnt = 0

        # Iterate over ranges (default changeset view is always one changeset)
        for changeset in c.cs_ranges:
            inlines = []
            if method == 'show':
                c.statuses.extend([ChangesetStatusModel().get_status(
                            c.rhodecode_db_repo.repo_id, changeset.raw_id)])

                c.comments.extend(ChangesetCommentsModel()\
                                  .get_comments(c.rhodecode_db_repo.repo_id,
                                                revision=changeset.raw_id))

                #comments from PR
                st = ChangesetStatusModel().get_statuses(
                            c.rhodecode_db_repo.repo_id, changeset.raw_id,
                            with_revisions=True)
                # from associated statuses, check the pull requests, and
                # show comments from them

                prs = set([x.pull_request for x in
                           filter(lambda x: x.pull_request != None, st)])

                for pr in prs:
                    c.comments.extend(pr.comments)
                inlines = ChangesetCommentsModel()\
                            .get_inline_comments(c.rhodecode_db_repo.repo_id,
                                                 revision=changeset.raw_id)
                c.inline_comments.extend(inlines)

            c.changes[changeset.raw_id] = []

            cs2 = changeset.raw_id
            cs1 = changeset.parents[0].raw_id if changeset.parents else EmptyChangeset()
            context_lcl = get_line_ctx('', request.GET)
            ign_whitespace_lcl = ign_whitespace_lcl = get_ignore_ws('', request.GET)

            _diff = c.rhodecode_repo.get_diff(cs1, cs2,
                ignore_whitespace=ign_whitespace_lcl, context=context_lcl)
            diff_limit = self.cut_off_limit if not fulldiff else None
            diff_processor = diffs.DiffProcessor(_diff,
                                                 vcs=c.rhodecode_repo.alias,
                                                 format='gitdiff',
                                                 diff_limit=diff_limit)
            cs_changes = OrderedDict()
            if method == 'show':
                _parsed = diff_processor.prepare()
                c.limited_diff = False
                if isinstance(_parsed, LimitedDiffContainer):
                    c.limited_diff = True
                for f in _parsed:
                    st = f['stats']
                    if st[0] != 'b':
                        c.lines_added += st[0]
                        c.lines_deleted += st[1]
                    fid = h.FID(changeset.raw_id, f['filename'])
                    diff = diff_processor.as_html(enable_comments=enable_comments,
                                                  parsed_lines=[f])
                    cs_changes[fid] = [cs1, cs2, f['operation'], f['filename'],
                                       diff, st]
            else:
                # downloads/raw we only need RAW diff nothing else
                diff = diff_processor.as_raw()
                cs_changes[''] = [None, None, None, None, diff, None]
            c.changes[changeset.raw_id] = cs_changes

        #sort comments by how they were generated
        c.comments = sorted(c.comments, key=lambda x: x.comment_id)

        # count inline comments
        for __, lines in c.inline_comments:
            for comments in lines.values():
                c.inline_cnt += len(comments)

        if len(c.cs_ranges) == 1:
            c.changeset = c.cs_ranges[0]
            c.parent_tmpl = ''.join(['# Parent  %s\n' % x.raw_id
                                     for x in c.changeset.parents])
        if method == 'download':
            response.content_type = 'text/plain'
            response.content_disposition = 'attachment; filename=%s.diff' \
                                            % revision[:12]
            return diff
        elif method == 'patch':
            response.content_type = 'text/plain'
            c.diff = safe_unicode(diff)
            return render('changeset/patch_changeset.html')
        elif method == 'raw':
            response.content_type = 'text/plain'
            return diff
        elif method == 'show':
            if len(c.cs_ranges) == 1:
                return render('changeset/changeset.html')
            else:
                return render('changeset/changeset_range.html')
Example #30
0
    def set_status(self,
                   repo,
                   status,
                   user,
                   comment=None,
                   revision=None,
                   pull_request=None,
                   dont_allow_on_closed_pull_request=False):
        """
        Creates new status for changeset or updates the old ones bumping their
        version, leaving the current status at

        :param repo:
        :param revision:
        :param status:
        :param user:
        :param comment:
        :param dont_allow_on_closed_pull_request: don't allow a status change
            if last status was for pull request and it's closed. We shouldn't
            mess around this manually
        """
        repo = self._get_repo(repo)

        q = ChangesetStatus.query()
        if not comment:
            from rhodecode.model.comment import ChangesetCommentsModel
            comment = ChangesetCommentsModel().create(
                text='Auto status change to %s' %
                (ChangesetStatus.get_status_lbl(status)),
                repo=repo,
                user=user,
                pull_request=pull_request,
                send_email=False)
        if revision:
            q = q.filter(ChangesetStatus.repo == repo)
            q = q.filter(ChangesetStatus.revision == revision)
        elif pull_request:
            pull_request = self.__get_pull_request(pull_request)
            q = q.filter(ChangesetStatus.repo == pull_request.org_repo)
            q = q.filter(ChangesetStatus.revision.in_(pull_request.revisions))
        cur_statuses = q.all()

        #if statuses exists and last is associated with a closed pull request
        # we need to check if we can allow this status change
        if (dont_allow_on_closed_pull_request and cur_statuses
                and getattr(cur_statuses[0].pull_request, 'status',
                            '') == PullRequest.STATUS_CLOSED):
            raise StatusChangeOnClosedPullRequestError(
                'Changing status on closed pull request is not allowed')

        #update all current statuses with older version
        if cur_statuses:
            for st in cur_statuses:
                st.version += 1
                self.sa.add(st)

        def _create_status(user, repo, status, comment, revision,
                           pull_request):
            new_status = ChangesetStatus()
            new_status.author = self._get_user(user)
            new_status.repo = self._get_repo(repo)
            new_status.status = status
            new_status.comment = comment
            new_status.revision = revision
            new_status.pull_request = pull_request
            return new_status

        if revision:
            new_status = _create_status(user=user,
                                        repo=repo,
                                        status=status,
                                        comment=comment,
                                        revision=revision,
                                        pull_request=None)
            self.sa.add(new_status)
            return new_status
        elif pull_request:
            #pull request can have more than one revision associated to it
            #we need to create new version for each one
            new_statuses = []
            repo = pull_request.org_repo
            for rev in pull_request.revisions:
                new_status = _create_status(user=user,
                                            repo=repo,
                                            status=status,
                                            comment=comment,
                                            revision=rev,
                                            pull_request=pull_request)
                new_statuses.append(new_status)
                self.sa.add(new_status)
            return new_statuses