##// END OF EJS Templates
- added commenting to pull requests...
marcink -
r2443:fd0a8224 codereview
parent child Browse files
Show More
@@ -457,6 +457,12 b' def make_map(config):'
457 action='show_all', conditions=dict(function=check_repo,
457 action='show_all', conditions=dict(function=check_repo,
458 method=["GET"]))
458 method=["GET"]))
459
459
460 rmap.connect('pullrequest_comment',
461 '/{repo_name:.*}/pull-request-comment/{pull_request_id}',
462 controller='pullrequests',
463 action='comment', conditions=dict(function=check_repo,
464 method=["POST"]))
465
460 rmap.connect('summary_home', '/{repo_name:.*}/summary',
466 rmap.connect('summary_home', '/{repo_name:.*}/summary',
461 controller='summary', conditions=dict(function=check_repo))
467 controller='summary', conditions=dict(function=check_repo))
462
468
@@ -390,10 +390,10 b' class ChangesetController(BaseRepoContro'
390 if status and change_status:
390 if status and change_status:
391 ChangesetStatusModel().set_status(
391 ChangesetStatusModel().set_status(
392 c.rhodecode_db_repo.repo_id,
392 c.rhodecode_db_repo.repo_id,
393 revision,
394 status,
393 status,
395 c.rhodecode_user.user_id,
394 c.rhodecode_user.user_id,
396 comm,
395 comm,
396 revision=revision,
397 )
397 )
398 action_logger(self.rhodecode_user,
398 action_logger(self.rhodecode_user,
399 'user_commented_revision:%s' % revision,
399 'user_commented_revision:%s' % revision,
@@ -24,19 +24,20 b''
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import logging
25 import logging
26 import traceback
26 import traceback
27 import binascii
28
27
29 from webob.exc import HTTPNotFound
28 from webob.exc import HTTPNotFound
30
29
31 from pylons import request, response, session, tmpl_context as c, url
30 from pylons import request, response, session, tmpl_context as c, url
32 from pylons.controllers.util import abort, redirect
31 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from pylons.decorators import jsonify
34
34
35 from rhodecode.lib.base import BaseRepoController, render
35 from rhodecode.lib.base import BaseRepoController, render
36 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib import diffs
38 from rhodecode.lib import diffs
39 from rhodecode.model.db import User, PullRequest, Repository, ChangesetStatus
39 from rhodecode.lib.utils import action_logger
40 from rhodecode.model.db import User, PullRequest, ChangesetStatus
40 from rhodecode.model.pull_request import PullRequestModel
41 from rhodecode.model.pull_request import PullRequestModel
41 from rhodecode.model.meta import Session
42 from rhodecode.model.meta import Session
42 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.repo import RepoModel
@@ -208,9 +209,56 b' class PullrequestsController(BaseRepoCon'
208 .get_comments(c.rhodecode_db_repo.repo_id,
209 .get_comments(c.rhodecode_db_repo.repo_id,
209 pull_request=pull_request_id)
210 pull_request=pull_request_id)
210
211
211 # changeset(pull-request) statuse
212 # changeset(pull-request) status
212 c.current_changeset_status = ChangesetStatusModel()\
213 c.current_changeset_status = ChangesetStatusModel()\
213 .get_status(c.rhodecode_db_repo.repo_id,
214 .get_status(c.pull_request.org_repo,
214 pull_request=pull_request_id)
215 pull_request=c.pull_request)
215 c.changeset_statuses = ChangesetStatus.STATUSES
216 c.changeset_statuses = ChangesetStatus.STATUSES
216 return render('/pullrequests/pullrequest_show.html')
217 return render('/pullrequests/pullrequest_show.html')
218
219 @jsonify
220 def comment(self, repo_name, pull_request_id):
221
222 status = request.POST.get('changeset_status')
223 change_status = request.POST.get('change_changeset_status')
224
225 comm = ChangesetCommentsModel().create(
226 text=request.POST.get('text'),
227 repo_id=c.rhodecode_db_repo.repo_id,
228 user_id=c.rhodecode_user.user_id,
229 pull_request=pull_request_id,
230 f_path=request.POST.get('f_path'),
231 line_no=request.POST.get('line'),
232 status_change=(ChangesetStatus.get_status_lbl(status)
233 if status and change_status else None)
234 )
235
236 # get status if set !
237 if status and change_status:
238 ChangesetStatusModel().set_status(
239 c.rhodecode_db_repo.repo_id,
240 status,
241 c.rhodecode_user.user_id,
242 comm,
243 pull_request=pull_request_id
244 )
245 action_logger(self.rhodecode_user,
246 'user_commented_pull_request:%s' % pull_request_id,
247 c.rhodecode_db_repo, self.ip_addr, self.sa)
248
249 Session.commit()
250
251 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
252 return redirect(h.url('pullrequest_show', repo_name=repo_name,
253 pull_request_id=pull_request_id))
254
255 data = {
256 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
257 }
258 if comm:
259 c.co = comm
260 data.update(comm.get_dict())
261 data.update({'rendered_text':
262 render('changeset/changeset_comment_block.html')})
263
264 return data No newline at end of file
@@ -66,12 +66,15 b' class ChangesetStatusModel(BaseModel):'
66 else:
66 else:
67 raise Exception('Please specify revision or pull_request')
67 raise Exception('Please specify revision or pull_request')
68
68
69 status = q.scalar()
69 # need to use first here since there can be multiple statuses
70 # returned from pull_request
71 status = q.first()
70 status = status.status if status else status
72 status = status.status if status else status
71 st = status or ChangesetStatus.DEFAULT
73 st = status or ChangesetStatus.DEFAULT
72 return str(st)
74 return str(st)
73
75
74 def set_status(self, repo, revision, status, user, comment):
76 def set_status(self, repo, status, user, comment, revision=None,
77 pull_request=None):
75 """
78 """
76 Creates new status for changeset or updates the old ones bumping their
79 Creates new status for changeset or updates the old ones bumping their
77 version, leaving the current status at
80 version, leaving the current status at
@@ -89,20 +92,48 b' class ChangesetStatusModel(BaseModel):'
89 """
92 """
90 repo = self._get_repo(repo)
93 repo = self._get_repo(repo)
91
94
92 cur_statuses = ChangesetStatus.query()\
95 q = ChangesetStatus.query()
93 .filter(ChangesetStatus.repo == repo)\
96
94 .filter(ChangesetStatus.revision == revision)\
97 if revision:
95 .all()
98 q = q.filter(ChangesetStatus.repo == repo)
99 q = q.filter(ChangesetStatus.revision == revision)
100 elif pull_request:
101 pull_request = self.__get_pull_request(pull_request)
102 q = q.filter(ChangesetStatus.repo == pull_request.org_repo)
103 q = q.filter(ChangesetStatus.pull_request == pull_request)
104 cur_statuses = q.all()
105
96 if cur_statuses:
106 if cur_statuses:
97 for st in cur_statuses:
107 for st in cur_statuses:
98 st.version += 1
108 st.version += 1
99 self.sa.add(st)
109 self.sa.add(st)
100 new_status = ChangesetStatus()
110
101 new_status.author = self._get_user(user)
111 def _create_status(user, repo, status, comment, revision, pull_request):
102 new_status.repo = self._get_repo(repo)
112 new_status = ChangesetStatus()
103 new_status.status = status
113 new_status.author = self._get_user(user)
104 new_status.revision = revision
114 new_status.repo = self._get_repo(repo)
105 new_status.comment = comment
115 new_status.status = status
106 self.sa.add(new_status)
116 new_status.comment = comment
107 return new_status
117 new_status.revision = revision
118 new_status.pull_request = pull_request
119 return new_status
108
120
121 if revision:
122 new_status = _create_status(user=user, repo=repo, status=status,
123 comment=comment, revision=revision,
124 pull_request=None)
125 self.sa.add(new_status)
126 return new_status
127 elif pull_request:
128 #pull request can have more than one revision associated to it
129 #we need to create new version for each one
130 new_statuses = []
131 repo = pull_request.org_repo
132 for rev in pull_request.revisions:
133 new_status = _create_status(user=user, repo=repo,
134 status=status, comment=comment,
135 revision=rev,
136 pull_request=pull_request)
137 new_statuses.append(new_status)
138 self.sa.add(new_status)
139 return new_statuses
@@ -55,38 +55,54 b' class ChangesetCommentsModel(BaseModel):'
55 user_objects.append(user_obj)
55 user_objects.append(user_obj)
56 return user_objects
56 return user_objects
57
57
58 def create(self, text, repo_id, user_id, revision, f_path=None,
58 def create(self, text, repo_id, user_id, revision=None, pull_request=None,
59 line_no=None, status_change=None):
59 f_path=None, line_no=None, status_change=None):
60 """
60 """
61 Creates new comment for changeset. IF status_change is not none
61 Creates new comment for changeset or pull request.
62 this comment is associated with a status change of changeset
62 IF status_change is not none this comment is associated with a
63 status change of changeset or changesets associated with pull request
63
64
64 :param text:
65 :param text:
65 :param repo_id:
66 :param repo_id:
66 :param user_id:
67 :param user_id:
67 :param revision:
68 :param revision:
69 :param pull_request:
68 :param f_path:
70 :param f_path:
69 :param line_no:
71 :param line_no:
70 :param status_change:
72 :param status_change:
71 """
73 """
74 if not text:
75 return
72
76
73 if text:
77 repo = Repository.get(repo_id)
74 repo = Repository.get(repo_id)
78 comment = ChangesetComment()
79 comment.repo = repo
80 comment.user_id = user_id
81 comment.text = text
82 comment.f_path = f_path
83 comment.line_no = line_no
84
85 if revision:
75 cs = repo.scm_instance.get_changeset(revision)
86 cs = repo.scm_instance.get_changeset(revision)
76 desc = "%s - %s" % (cs.short_id, h.shorter(cs.message, 256))
87 desc = "%s - %s" % (cs.short_id, h.shorter(cs.message, 256))
77 author_email = cs.author_email
88 author_email = cs.author_email
78 comment = ChangesetComment()
79 comment.repo = repo
80 comment.user_id = user_id
81 comment.revision = revision
89 comment.revision = revision
82 comment.text = text
90 elif pull_request:
83 comment.f_path = f_path
91 pull_request = self.__get_pull_request(pull_request)
84 comment.line_no = line_no
92 comment.pull_request = pull_request
93 desc = ''
94 else:
95 raise Exception('Please specify revision or pull_request_id')
85
96
86 self.sa.add(comment)
97 self.sa.add(comment)
87 self.sa.flush()
98 self.sa.flush()
88 # make notification
99
89 line = ''
100 # make notification
101 line = ''
102 body = text
103
104 #changeset
105 if revision:
90 if line_no:
106 if line_no:
91 line = _('on line %s') % line_no
107 line = _('on line %s') % line_no
92 subj = safe_unicode(
108 subj = safe_unicode(
@@ -99,34 +115,41 b' class ChangesetCommentsModel(BaseModel):'
99 )
115 )
100 )
116 )
101 )
117 )
102
118 notification_type = Notification.TYPE_CHANGESET_COMMENT
103 body = text
104
105 # get the current participants of this changeset
119 # get the current participants of this changeset
106 recipients = ChangesetComment.get_users(revision=revision)
120 recipients = ChangesetComment.get_users(revision=revision)
107
108 # add changeset author if it's in rhodecode system
121 # add changeset author if it's in rhodecode system
109 recipients += [User.get_by_email(author_email)]
122 recipients += [User.get_by_email(author_email)]
123 #pull request
124 elif pull_request:
125 #TODO: make this something usefull
126 subj = 'commented on pull request something...'
127 notification_type = Notification.TYPE_PULL_REQUEST_COMMENT
128 # get the current participants of this pull request
129 recipients = ChangesetComment.get_users(pull_request_id=
130 pull_request.pull_request_id)
131 # add pull request author
132 recipients += [pull_request.author]
110
133
111 # create notification objects, and emails
134 # create notification objects, and emails
135 NotificationModel().create(
136 created_by=user_id, subject=subj, body=body,
137 recipients=recipients, type_=notification_type,
138 email_kwargs={'status_change': status_change}
139 )
140
141 mention_recipients = set(self._extract_mentions(body))\
142 .difference(recipients)
143 if mention_recipients:
144 subj = _('[Mention]') + ' ' + subj
112 NotificationModel().create(
145 NotificationModel().create(
113 created_by=user_id, subject=subj, body=body,
146 created_by=user_id, subject=subj, body=body,
114 recipients=recipients, type_=Notification.TYPE_CHANGESET_COMMENT,
147 recipients=mention_recipients,
115 email_kwargs={'status_change': status_change}
148 type_=notification_type,
149 email_kwargs={'status_change': status_change}
116 )
150 )
117
151
118 mention_recipients = set(self._extract_mentions(body))\
152 return comment
119 .difference(recipients)
120 if mention_recipients:
121 subj = _('[Mention]') + ' ' + subj
122 NotificationModel().create(
123 created_by=user_id, subject=subj, body=body,
124 recipients=mention_recipients,
125 type_=Notification.TYPE_CHANGESET_COMMENT,
126 email_kwargs={'status_change': status_change}
127 )
128
129 return comment
130
153
131 def delete(self, comment):
154 def delete(self, comment):
132 """
155 """
@@ -766,7 +766,12 b' class Repository(Base, BaseModel):'
766 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
766 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
767 grouped = {}
767 grouped = {}
768 for stat in statuses.all():
768 for stat in statuses.all():
769 grouped[stat.revision] = [str(stat.status), stat.status_lbl]
769 pr_id = pr_repo = None
770 if stat.pull_request:
771 pr_id = stat.pull_request.pull_request_id
772 pr_repo = stat.pull_request.other_repo.repo_name
773 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
774 pr_id, pr_repo]
770 return grouped
775 return grouped
771
776
772 #==========================================================================
777 #==========================================================================
@@ -1336,17 +1341,21 b' class ChangesetComment(Base, BaseModel):'
1336 pull_request = relationship('PullRequest', lazy='joined')
1341 pull_request = relationship('PullRequest', lazy='joined')
1337
1342
1338 @classmethod
1343 @classmethod
1339 def get_users(cls, revision):
1344 def get_users(cls, revision=None, pull_request_id=None):
1340 """
1345 """
1341 Returns user associated with this changesetComment. ie those
1346 Returns user associated with this ChangesetComment. ie those
1342 who actually commented
1347 who actually commented
1343
1348
1344 :param cls:
1349 :param cls:
1345 :param revision:
1350 :param revision:
1346 """
1351 """
1347 return Session.query(User)\
1352 q = Session.query(User)\
1348 .filter(cls.revision == revision)\
1353 .join(ChangesetComment.author)
1349 .join(ChangesetComment.author).all()
1354 if revision:
1355 q = q.filter(cls.revision == revision)
1356 elif pull_request_id:
1357 q = q.filter(cls.pull_request_id == pull_request_id)
1358 return q.all()
1350
1359
1351
1360
1352 class ChangesetStatus(Base, BaseModel):
1361 class ChangesetStatus(Base, BaseModel):
@@ -1457,6 +1466,7 b' class Notification(Base, BaseModel):'
1457 TYPE_MENTION = u'mention'
1466 TYPE_MENTION = u'mention'
1458 TYPE_REGISTRATION = u'registration'
1467 TYPE_REGISTRATION = u'registration'
1459 TYPE_PULL_REQUEST = u'pull_request'
1468 TYPE_PULL_REQUEST = u'pull_request'
1469 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1460
1470
1461 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1471 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1462 subject = Column('subject', Unicode(512), nullable=True)
1472 subject = Column('subject', Unicode(512), nullable=True)
@@ -198,13 +198,15 b' class NotificationModel(BaseModel):'
198 Creates a human readable description based on properties
198 Creates a human readable description based on properties
199 of notification object
199 of notification object
200 """
200 """
201
201 #alias
202 _n = notification
202 _map = {
203 _map = {
203 notification.TYPE_CHANGESET_COMMENT: _('commented on commit'),
204 _n.TYPE_CHANGESET_COMMENT: _('commented on commit'),
204 notification.TYPE_MESSAGE: _('sent message'),
205 _n.TYPE_MESSAGE: _('sent message'),
205 notification.TYPE_MENTION: _('mentioned you'),
206 _n.TYPE_MENTION: _('mentioned you'),
206 notification.TYPE_REGISTRATION: _('registered in RhodeCode'),
207 _n.TYPE_REGISTRATION: _('registered in RhodeCode'),
207 notification.TYPE_PULL_REQUEST: _('opened new pull request')
208 _n.TYPE_PULL_REQUEST: _('opened new pull request'),
209 _n.TYPE_PULL_REQUEST_COMMENT: _('commented on pull request')
208 }
210 }
209
211
210 tmpl = "%(user)s %(action)s %(when)s"
212 tmpl = "%(user)s %(action)s %(when)s"
@@ -85,7 +85,13 b''
85 <div class="changeset-status-container">
85 <div class="changeset-status-container">
86 %if c.statuses.get(cs.raw_id):
86 %if c.statuses.get(cs.raw_id):
87 <div title="${_('Changeset status')}" class="changeset-status-lbl">${c.statuses.get(cs.raw_id)[1]}</div>
87 <div title="${_('Changeset status')}" class="changeset-status-lbl">${c.statuses.get(cs.raw_id)[1]}</div>
88 <div class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" /></div>
88 <div class="changeset-status-ico">
89 %if c.statuses.get(cs.raw_id)[2]:
90 <a class="tooltip" title="${_('Click to open associated pull request')}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" /></a>
91 %else:
92 <img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" />
93 %endif
94 </div>
89 %endif
95 %endif
90 </div>
96 </div>
91 </div>
97 </div>
@@ -70,8 +70,8 b''
70 ##${comment.comment_inline_form(c.changeset)}
70 ##${comment.comment_inline_form(c.changeset)}
71
71
72 ## render comments main comments form and it status
72 ## render comments main comments form and it status
73 ##${comment.comments(h.url('pull_request_comment', repo_name=c.repo_name, pull_request_id=c.pull_request.pull_request_id),
73 ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name, pull_request_id=c.pull_request.pull_request_id),
74 ## c.current_changeset_status)}
74 c.current_changeset_status)}
75
75
76 </div>
76 </div>
77
77
General Comments 0
You need to be logged in to leave comments. Login now