diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py --- a/rhodecode/config/routing.py +++ b/rhodecode/config/routing.py @@ -457,6 +457,12 @@ def make_map(config): action='show_all', conditions=dict(function=check_repo, method=["GET"])) + rmap.connect('pullrequest_comment', + '/{repo_name:.*}/pull-request-comment/{pull_request_id}', + controller='pullrequests', + action='comment', conditions=dict(function=check_repo, + method=["POST"])) + rmap.connect('summary_home', '/{repo_name:.*}/summary', controller='summary', conditions=dict(function=check_repo)) diff --git a/rhodecode/controllers/changeset.py b/rhodecode/controllers/changeset.py --- a/rhodecode/controllers/changeset.py +++ b/rhodecode/controllers/changeset.py @@ -390,10 +390,10 @@ class ChangesetController(BaseRepoContro if status and change_status: ChangesetStatusModel().set_status( c.rhodecode_db_repo.repo_id, - revision, status, c.rhodecode_user.user_id, comm, + revision=revision, ) action_logger(self.rhodecode_user, 'user_commented_revision:%s' % revision, diff --git a/rhodecode/controllers/pullrequests.py b/rhodecode/controllers/pullrequests.py --- a/rhodecode/controllers/pullrequests.py +++ b/rhodecode/controllers/pullrequests.py @@ -24,19 +24,20 @@ # along with this program. If not, see . import logging import traceback -import binascii from webob.exc import HTTPNotFound from pylons import request, response, session, tmpl_context as c, url from pylons.controllers.util import abort, redirect from pylons.i18n.translation import _ +from pylons.decorators import jsonify from rhodecode.lib.base import BaseRepoController, render from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator from rhodecode.lib import helpers as h from rhodecode.lib import diffs -from rhodecode.model.db import User, PullRequest, Repository, ChangesetStatus +from rhodecode.lib.utils import action_logger +from rhodecode.model.db import User, PullRequest, ChangesetStatus from rhodecode.model.pull_request import PullRequestModel from rhodecode.model.meta import Session from rhodecode.model.repo import RepoModel @@ -208,9 +209,56 @@ class PullrequestsController(BaseRepoCon .get_comments(c.rhodecode_db_repo.repo_id, pull_request=pull_request_id) - # changeset(pull-request) statuse + # changeset(pull-request) status c.current_changeset_status = ChangesetStatusModel()\ - .get_status(c.rhodecode_db_repo.repo_id, - pull_request=pull_request_id) + .get_status(c.pull_request.org_repo, + pull_request=c.pull_request) c.changeset_statuses = ChangesetStatus.STATUSES return render('/pullrequests/pullrequest_show.html') + + @jsonify + def comment(self, repo_name, pull_request_id): + + status = request.POST.get('changeset_status') + change_status = request.POST.get('change_changeset_status') + + comm = ChangesetCommentsModel().create( + text=request.POST.get('text'), + repo_id=c.rhodecode_db_repo.repo_id, + user_id=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) + + 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 \ No newline at end of file diff --git a/rhodecode/model/changeset_status.py b/rhodecode/model/changeset_status.py --- a/rhodecode/model/changeset_status.py +++ b/rhodecode/model/changeset_status.py @@ -66,12 +66,15 @@ class ChangesetStatusModel(BaseModel): else: raise Exception('Please specify revision or pull_request') - status = q.scalar() + # need to use first here since there can be multiple statuses + # returned from pull_request + status = q.first() status = status.status if status else status st = status or ChangesetStatus.DEFAULT return str(st) - def set_status(self, repo, revision, status, user, comment): + 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 @@ -89,20 +92,48 @@ class ChangesetStatusModel(BaseModel): """ repo = self._get_repo(repo) - cur_statuses = ChangesetStatus.query()\ - .filter(ChangesetStatus.repo == repo)\ - .filter(ChangesetStatus.revision == revision)\ - .all() + q = ChangesetStatus.query() + + 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.pull_request == pull_request) + cur_statuses = q.all() + if cur_statuses: for st in cur_statuses: st.version += 1 self.sa.add(st) - new_status = ChangesetStatus() - new_status.author = self._get_user(user) - new_status.repo = self._get_repo(repo) - new_status.status = status - new_status.revision = revision - new_status.comment = comment - self.sa.add(new_status) - return new_status + + 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 diff --git a/rhodecode/model/comment.py b/rhodecode/model/comment.py --- a/rhodecode/model/comment.py +++ b/rhodecode/model/comment.py @@ -55,38 +55,54 @@ class ChangesetCommentsModel(BaseModel): user_objects.append(user_obj) return user_objects - def create(self, text, repo_id, user_id, revision, f_path=None, - line_no=None, status_change=None): + def create(self, text, repo_id, user_id, revision=None, pull_request=None, + f_path=None, line_no=None, status_change=None): """ - Creates new comment for changeset. IF status_change is not none - this comment is associated with a status change of changeset + Creates new comment for changeset or pull request. + IF status_change is not none this comment is associated with a + status change of changeset or changesets associated with pull request :param text: :param repo_id: :param user_id: :param revision: + :param pull_request: :param f_path: :param line_no: :param status_change: """ + if not text: + return - if text: - repo = Repository.get(repo_id) + repo = Repository.get(repo_id) + comment = ChangesetComment() + comment.repo = repo + comment.user_id = user_id + comment.text = text + comment.f_path = f_path + comment.line_no = line_no + + if revision: cs = repo.scm_instance.get_changeset(revision) desc = "%s - %s" % (cs.short_id, h.shorter(cs.message, 256)) author_email = cs.author_email - comment = ChangesetComment() - comment.repo = repo - comment.user_id = user_id comment.revision = revision - comment.text = text - comment.f_path = f_path - comment.line_no = line_no + elif pull_request: + pull_request = self.__get_pull_request(pull_request) + comment.pull_request = pull_request + desc = '' + else: + raise Exception('Please specify revision or pull_request_id') - self.sa.add(comment) - self.sa.flush() - # make notification - line = '' + self.sa.add(comment) + self.sa.flush() + + # make notification + line = '' + body = text + + #changeset + if revision: if line_no: line = _('on line %s') % line_no subj = safe_unicode( @@ -99,34 +115,41 @@ class ChangesetCommentsModel(BaseModel): ) ) ) - - body = text - + notification_type = Notification.TYPE_CHANGESET_COMMENT # get the current participants of this changeset recipients = ChangesetComment.get_users(revision=revision) - # add changeset author if it's in rhodecode system recipients += [User.get_by_email(author_email)] + #pull request + elif pull_request: + #TODO: make this something usefull + subj = 'commented on pull request something...' + notification_type = Notification.TYPE_PULL_REQUEST_COMMENT + # get the current participants of this pull request + recipients = ChangesetComment.get_users(pull_request_id= + pull_request.pull_request_id) + # add pull request author + recipients += [pull_request.author] - # create notification objects, and emails + # create notification objects, and emails + NotificationModel().create( + created_by=user_id, subject=subj, body=body, + recipients=recipients, type_=notification_type, + email_kwargs={'status_change': status_change} + ) + + mention_recipients = set(self._extract_mentions(body))\ + .difference(recipients) + if mention_recipients: + subj = _('[Mention]') + ' ' + subj NotificationModel().create( - created_by=user_id, subject=subj, body=body, - recipients=recipients, type_=Notification.TYPE_CHANGESET_COMMENT, - email_kwargs={'status_change': status_change} + created_by=user_id, subject=subj, body=body, + recipients=mention_recipients, + type_=notification_type, + email_kwargs={'status_change': status_change} ) - mention_recipients = set(self._extract_mentions(body))\ - .difference(recipients) - if mention_recipients: - subj = _('[Mention]') + ' ' + subj - NotificationModel().create( - created_by=user_id, subject=subj, body=body, - recipients=mention_recipients, - type_=Notification.TYPE_CHANGESET_COMMENT, - email_kwargs={'status_change': status_change} - ) - - return comment + return comment def delete(self, comment): """ diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -766,7 +766,12 @@ class Repository(Base, BaseModel): statuses = statuses.filter(ChangesetStatus.revision.in_(revisions)) grouped = {} for stat in statuses.all(): - grouped[stat.revision] = [str(stat.status), stat.status_lbl] + pr_id = pr_repo = None + if stat.pull_request: + pr_id = stat.pull_request.pull_request_id + pr_repo = stat.pull_request.other_repo.repo_name + grouped[stat.revision] = [str(stat.status), stat.status_lbl, + pr_id, pr_repo] return grouped #========================================================================== @@ -1336,17 +1341,21 @@ class ChangesetComment(Base, BaseModel): pull_request = relationship('PullRequest', lazy='joined') @classmethod - def get_users(cls, revision): + def get_users(cls, revision=None, pull_request_id=None): """ - Returns user associated with this changesetComment. ie those + Returns user associated with this ChangesetComment. ie those who actually commented :param cls: :param revision: """ - return Session.query(User)\ - .filter(cls.revision == revision)\ - .join(ChangesetComment.author).all() + q = Session.query(User)\ + .join(ChangesetComment.author) + if revision: + q = q.filter(cls.revision == revision) + elif pull_request_id: + q = q.filter(cls.pull_request_id == pull_request_id) + return q.all() class ChangesetStatus(Base, BaseModel): @@ -1457,6 +1466,7 @@ class Notification(Base, BaseModel): TYPE_MENTION = u'mention' TYPE_REGISTRATION = u'registration' TYPE_PULL_REQUEST = u'pull_request' + TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment' notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True) subject = Column('subject', Unicode(512), nullable=True) diff --git a/rhodecode/model/notification.py b/rhodecode/model/notification.py --- a/rhodecode/model/notification.py +++ b/rhodecode/model/notification.py @@ -198,13 +198,15 @@ class NotificationModel(BaseModel): Creates a human readable description based on properties of notification object """ - + #alias + _n = notification _map = { - notification.TYPE_CHANGESET_COMMENT: _('commented on commit'), - notification.TYPE_MESSAGE: _('sent message'), - notification.TYPE_MENTION: _('mentioned you'), - notification.TYPE_REGISTRATION: _('registered in RhodeCode'), - notification.TYPE_PULL_REQUEST: _('opened new pull request') + _n.TYPE_CHANGESET_COMMENT: _('commented on commit'), + _n.TYPE_MESSAGE: _('sent message'), + _n.TYPE_MENTION: _('mentioned you'), + _n.TYPE_REGISTRATION: _('registered in RhodeCode'), + _n.TYPE_PULL_REQUEST: _('opened new pull request'), + _n.TYPE_PULL_REQUEST_COMMENT: _('commented on pull request') } tmpl = "%(user)s %(action)s %(when)s" diff --git a/rhodecode/templates/changelog/changelog.html b/rhodecode/templates/changelog/changelog.html --- a/rhodecode/templates/changelog/changelog.html +++ b/rhodecode/templates/changelog/changelog.html @@ -85,7 +85,13 @@
%if c.statuses.get(cs.raw_id):
${c.statuses.get(cs.raw_id)[1]}
-
+
+ %if c.statuses.get(cs.raw_id)[2]: + + %else: + + %endif +
%endif
diff --git a/rhodecode/templates/pullrequests/pullrequest_show.html b/rhodecode/templates/pullrequests/pullrequest_show.html --- a/rhodecode/templates/pullrequests/pullrequest_show.html +++ b/rhodecode/templates/pullrequests/pullrequest_show.html @@ -70,8 +70,8 @@ ##${comment.comment_inline_form(c.changeset)} ## render comments main comments form and it status - ##${comment.comments(h.url('pull_request_comment', repo_name=c.repo_name, pull_request_id=c.pull_request.pull_request_id), - ## c.current_changeset_status)} + ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name, pull_request_id=c.pull_request.pull_request_id), + c.current_changeset_status)}