def set_status(self, repo, status, user, comment, 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 the value of 'status'. :param repo: :param status: :param user: :param comment: :param revision: :param pull_request: :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 revision is not None: assert pull_request is None q = q.filter(ChangesetStatus.repo == repo) q = q.filter(ChangesetStatus.revision == revision) revisions = [revision] else: assert pull_request is not None pull_request = self.__get_pull_request(pull_request) repo = pull_request.org_repo q = q.filter(ChangesetStatus.repo == repo) q = q.filter(ChangesetStatus.revision.in_(pull_request.revisions)) revisions = 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 for st in cur_statuses: st.version += 1 self.sa.add(st) new_statuses = [] for rev in revisions: new_status = ChangesetStatus() new_status.version = 0 # default 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 = rev new_status.pull_request = pull_request new_statuses.append(new_status) self.sa.add(new_status) return new_statuses
def set_status(self, repo, status, user, comment, revision=None, pull_request=None): """ Creates new status for changeset or updates the old ones bumping their version, leaving the current status at the value of 'status'. :param repo: :param status: :param user: :param comment: :param revision: :param pull_request: """ repo = Repository.guess_instance(repo) q = ChangesetStatus.query() if revision is not None: assert pull_request is None q = q.filter(ChangesetStatus.repo == repo) q = q.filter(ChangesetStatus.revision == revision) revisions = [revision] else: assert pull_request is not None pull_request = PullRequest.guess_instance(pull_request) repo = pull_request.org_repo q = q.filter(ChangesetStatus.repo == repo) q = q.filter(ChangesetStatus.revision.in_(pull_request.revisions)) revisions = pull_request.revisions cur_statuses = q.all() # update all current statuses with older version for st in cur_statuses: st.version += 1 new_statuses = [] for rev in revisions: new_status = ChangesetStatus() new_status.version = 0 # default new_status.author = User.guess_instance(user) new_status.repo = Repository.guess_instance(repo) new_status.status = status new_status.comment = comment new_status.revision = rev new_status.pull_request = pull_request new_statuses.append(new_status) Session().add(new_status) return new_statuses
def comment(self, repo_name, revision): status = request.POST.get('changeset_status') text = request.POST.get('text', '').strip() or _('No comments.') c.co = comm = ChangesetCommentsModel().create( text=text, repo=c.db_repo.repo_id, user=c.authuser.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 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.db_repo.repo_id, status, c.authuser.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.authuser, 'user_commented_revision:%s' % revision, c.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 comment(self, repo_name, revision): status = request.POST.get('changeset_status') text = request.POST.get('text', '').strip() or _('No comments.') c.co = comm = ChangesetCommentsModel().create( text=text, repo=c.db_repo.repo_id, user=c.authuser.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 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.db_repo.repo_id, status, c.authuser.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.authuser, 'user_commented_revision:%s' % revision, c.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 create_comment(text, status, f_path, line_no, revision=None, pull_request_id=None, closing_pr=None): """Comment functionality shared between changesets and pullrequests""" f_path = f_path or None line_no = line_no or None comment = ChangesetCommentsModel().create( text=text, repo=c.db_repo.repo_id, author=request.authuser.user_id, revision=revision, pull_request=pull_request_id, f_path=f_path, line_no=line_no, status_change=ChangesetStatus.get_status_lbl(status) if status else None, closing_pr=closing_pr, ) return comment
def _get_status_query(self, repo, revision, pull_request, with_revisions=False): repo = self._get_repo(repo) q = ChangesetStatus.query()\ .filter(ChangesetStatus.repo == repo) if not with_revisions: q = q.filter(ChangesetStatus.version == 0) if revision: q = q.filter(ChangesetStatus.revision == revision) elif pull_request: pull_request = self.__get_pull_request(pull_request) q = q.filter(ChangesetStatus.pull_request == pull_request) else: raise Exception('Please specify revision or pull_request') q = q.order_by(ChangesetStatus.version.asc()) return q
def _get_status_query(self, repo, revision, pull_request, with_revisions=False): repo = Repository.guess_instance(repo) q = ChangesetStatus.query() \ .filter(ChangesetStatus.repo == repo) if not with_revisions: # only report the latest vote across all users! TODO: be smarter! q = q.filter(ChangesetStatus.version == 0) if revision: q = q.filter(ChangesetStatus.revision == revision) elif pull_request: pull_request = PullRequest.guess_instance(pull_request) q = q.filter(ChangesetStatus.pull_request == pull_request) else: raise Exception('Please specify revision or pull_request') q = q.order_by(ChangesetStatus.version.asc()) return q
def create(self, created_by, org_repo, org_ref, other_repo, other_ref, revisions, reviewers, title, description=None): from kallithea.model.changeset_status import ChangesetStatusModel created_by_user = self._get_user(created_by) org_repo = self._get_repo(org_repo) other_repo = self._get_repo(other_repo) new = PullRequest() new.org_repo = org_repo new.org_ref = org_ref new.other_repo = other_repo new.other_ref = other_ref new.revisions = revisions new.title = title new.description = description new.author = created_by_user Session().add(new) Session().flush() #reset state to under-review from kallithea.model.comment import ChangesetCommentsModel comment = ChangesetCommentsModel().create( text=u'Auto status change to %s' % (ChangesetStatus.get_status_lbl(ChangesetStatus.STATUS_UNDER_REVIEW)), repo=org_repo, user=new.author, pull_request=new, send_email=False ) ChangesetStatusModel().set_status( org_repo, ChangesetStatus.STATUS_UNDER_REVIEW, new.author, comment, pull_request=new ) mention_recipients = set(User.get_by_username(username, case_insensitive=True) for username in extract_mentioned_users(new.description)) self.__add_reviewers(new, reviewers, mention_recipients) return new
def test_NotReviewedRevisions(self): repo_id = Repository.get_by_repo_name(HG_REPO).repo_id validator = v.NotReviewedRevisions(repo_id) rev = '0' * 40 # add status for a rev, that should throw an error because it is already # reviewed new_status = ChangesetStatus() new_status.author = ChangesetStatusModel()._get_user(TEST_USER_ADMIN_LOGIN) new_status.repo = ChangesetStatusModel()._get_repo(HG_REPO) new_status.status = ChangesetStatus.STATUS_APPROVED new_status.comment = None new_status.revision = rev Session().add(new_status) Session().commit() try: self.assertRaises(formencode.Invalid, validator.to_python, [rev]) finally: Session().delete(new_status) Session().commit()
def validate_python(self, value, state): # check revisions if they are not reviewed, or a part of another # pull request statuses = ChangesetStatus.query()\ .filter(ChangesetStatus.revision.in_(value))\ .filter(ChangesetStatus.repo_id == repo_id)\ .all() errors = [] for cs in statuses: if cs.pull_request_id: errors.append(['pull_req', cs.revision[:12]]) elif cs.status: errors.append(['status', cs.revision[:12]]) if errors: revs = ','.join([x[1] for x in errors]) msg = M(self, 'rev_already_reviewed', state, revs=revs) raise formencode.Invalid(msg, value, state, error_dict=dict(revisions=revs) )
def validate_python(self, value, state): # check revisions if they are not reviewed, or a part of another # pull request statuses = ChangesetStatus.query()\ .filter(ChangesetStatus.revision.in_(value))\ .filter(ChangesetStatus.repo_id == repo_id)\ .all() errors = [] for cs in statuses: if cs.pull_request_id: errors.append(['pull_req', cs.revision[:12]]) elif cs.status: errors.append(['status', cs.revision[:12]]) if errors: revs = ','.join([x[1] for x in errors]) msg = M(self, 'rev_already_reviewed', state, revs=revs) raise formencode.Invalid(msg, value, state, error_dict=dict(revisions=revs))
def comment(self, repo_name, pull_request_id): pull_request = PullRequest.get_or_404(pull_request_id) status = 0 close_pr = False allowed_to_change_status = self._get_is_allowed_change_status( pull_request) if allowed_to_change_status: status = request.POST.get('changeset_status') close_pr = request.POST.get('save_close') text = request.POST.get('text', '').strip() or _('No comments.') if close_pr: text = _('Closing.') + '\n' + text comm = ChangesetCommentsModel().create( text=text, repo=c.db_repo.repo_id, user=c.authuser.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) action_logger(self.authuser, 'user_commented_pull_request:%s' % pull_request_id, c.db_repo, self.ip_addr, self.sa) if allowed_to_change_status: # get status if set ! if status: ChangesetStatusModel().set_status(c.db_repo.repo_id, status, c.authuser.user_id, comm, pull_request=pull_request_id) if close_pr: PullRequestModel().close_pull_request(pull_request_id) action_logger(self.authuser, 'user_closed_pull_request:%s' % pull_request_id, c.db_repo, self.ip_addr, self.sa) Session().commit() if not request.environ.get('HTTP_X_PARTIAL_XHR'): return redirect(pull_request.url()) 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, pull_request_id): pull_request = PullRequest.get_or_404(pull_request_id) status = 0 close_pr = False allowed_to_change_status = self._get_is_allowed_change_status(pull_request) if allowed_to_change_status: status = request.POST.get('changeset_status') close_pr = request.POST.get('save_close') text = request.POST.get('text', '').strip() or _('No comments.') if close_pr: text = _('Closing.') + '\n' + text comm = ChangesetCommentsModel().create( text=text, repo=c.db_repo.repo_id, user=c.authuser.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 ) action_logger(self.authuser, 'user_commented_pull_request:%s' % pull_request_id, c.db_repo, self.ip_addr, self.sa) if allowed_to_change_status: # get status if set ! if status: ChangesetStatusModel().set_status( c.db_repo.repo_id, status, c.authuser.user_id, comm, pull_request=pull_request_id ) if close_pr: PullRequestModel().close_pull_request(pull_request_id) action_logger(self.authuser, 'user_closed_pull_request:%s' % pull_request_id, c.db_repo, self.ip_addr, self.sa) Session().commit() if not request.environ.get('HTTP_X_PARTIAL_XHR'): return redirect(pull_request.url()) 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 changeset_status_lbl(changeset_status): from kallithea.model.db import ChangesetStatus return ChangesetStatus.get_status_lbl(changeset_status)
def create_cs_pr_comment(repo_name, revision=None, pull_request=None, allowed_to_change_status=True): """ Add a comment to the specified changeset or pull request, using POST values from the request. Comments can be inline (when a file path and line number is specified in POST) or general comments. A comment can be accompanied by a review status change (accepted, rejected, etc.). Pull requests can be closed or deleted. Parameter 'allowed_to_change_status' is used for both status changes and closing of pull requests. For deleting of pull requests, more specific checks are done. """ assert request.environ.get('HTTP_X_PARTIAL_XHR') if pull_request: pull_request_id = pull_request.pull_request_id else: pull_request_id = None status = request.POST.get('changeset_status') close_pr = request.POST.get('save_close') delete = request.POST.get('save_delete') f_path = request.POST.get('f_path') line_no = request.POST.get('line') if (status or close_pr or delete) and (f_path or line_no): # status votes and closing is only possible in general comments raise HTTPBadRequest() if not allowed_to_change_status: if status or close_pr: h.flash(_('No permission to change status'), 'error') raise HTTPForbidden() if pull_request and delete == "delete": if (pull_request.owner_id == request.authuser.user_id or h.HasPermissionAny('hg.admin')() or h.HasRepoPermissionLevel('admin')( pull_request.org_repo.repo_name) or h.HasRepoPermissionLevel('admin')( pull_request.other_repo.repo_name) ) and not pull_request.is_closed(): PullRequestModel().delete(pull_request) Session().commit() h.flash(_('Successfully deleted pull request %s') % pull_request_id, category='success') return { 'location': h.url('my_pullrequests'), # or repo pr list? } raise HTTPForbidden() text = request.POST.get('text', '').strip() comment = ChangesetCommentsModel().create( text=text, repo=c.db_repo.repo_id, author=request.authuser.user_id, revision=revision, pull_request=pull_request_id, f_path=f_path or None, line_no=line_no or None, status_change=ChangesetStatus.get_status_lbl(status) if status else None, closing_pr=close_pr, ) if status: ChangesetStatusModel().set_status( c.db_repo.repo_id, status, request.authuser.user_id, comment, revision=revision, pull_request=pull_request_id, ) if pull_request: action = 'user_commented_pull_request:%s' % pull_request_id else: action = 'user_commented_revision:%s' % revision action_logger(request.authuser, action, c.db_repo, request.ip_addr) if pull_request and close_pr: PullRequestModel().close_pull_request(pull_request_id) action_logger(request.authuser, 'user_closed_pull_request:%s' % pull_request_id, c.db_repo, request.ip_addr) Session().commit() data = { 'target_id': h.safeid(request.POST.get('f_path')), } if comment is not None: c.comment = comment data.update(comment.get_dict()) data.update({ 'rendered_text': render('changeset/changeset_comment_block.html') }) return data