Show More
@@ -457,6 +457,12 b' def make_map(config):' | |||
|
457 | 457 | action='show_all', conditions=dict(function=check_repo, |
|
458 | 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 | 466 | rmap.connect('summary_home', '/{repo_name:.*}/summary', |
|
461 | 467 | controller='summary', conditions=dict(function=check_repo)) |
|
462 | 468 |
@@ -390,10 +390,10 b' class ChangesetController(BaseRepoContro' | |||
|
390 | 390 | if status and change_status: |
|
391 | 391 | ChangesetStatusModel().set_status( |
|
392 | 392 | c.rhodecode_db_repo.repo_id, |
|
393 | revision, | |
|
394 | 393 | status, |
|
395 | 394 | c.rhodecode_user.user_id, |
|
396 | 395 | comm, |
|
396 | revision=revision, | |
|
397 | 397 | ) |
|
398 | 398 | action_logger(self.rhodecode_user, |
|
399 | 399 | 'user_commented_revision:%s' % revision, |
@@ -24,19 +24,20 b'' | |||
|
24 | 24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
25 | 25 | import logging |
|
26 | 26 | import traceback |
|
27 | import binascii | |
|
28 | 27 | |
|
29 | 28 | from webob.exc import HTTPNotFound |
|
30 | 29 | |
|
31 | 30 | from pylons import request, response, session, tmpl_context as c, url |
|
32 | 31 | from pylons.controllers.util import abort, redirect |
|
33 | 32 | from pylons.i18n.translation import _ |
|
33 | from pylons.decorators import jsonify | |
|
34 | 34 | |
|
35 | 35 | from rhodecode.lib.base import BaseRepoController, render |
|
36 | 36 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator |
|
37 | 37 | from rhodecode.lib import helpers as h |
|
38 | 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 | 41 | from rhodecode.model.pull_request import PullRequestModel |
|
41 | 42 | from rhodecode.model.meta import Session |
|
42 | 43 | from rhodecode.model.repo import RepoModel |
@@ -208,9 +209,56 b' class PullrequestsController(BaseRepoCon' | |||
|
208 | 209 | .get_comments(c.rhodecode_db_repo.repo_id, |
|
209 | 210 | pull_request=pull_request_id) |
|
210 | 211 | |
|
211 |
# changeset(pull-request) status |
|
|
212 | # changeset(pull-request) status | |
|
212 | 213 | c.current_changeset_status = ChangesetStatusModel()\ |
|
213 |
.get_status(c. |
|
|
214 |
pull_request=pull_request |
|
|
214 | .get_status(c.pull_request.org_repo, | |
|
215 | pull_request=c.pull_request) | |
|
215 | 216 | c.changeset_statuses = ChangesetStatus.STATUSES |
|
216 | 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 | 66 | else: |
|
67 | 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 | 72 | status = status.status if status else status |
|
71 | 73 | st = status or ChangesetStatus.DEFAULT |
|
72 | 74 | return str(st) |
|
73 | 75 | |
|
74 |
def set_status(self, repo, |
|
|
76 | def set_status(self, repo, status, user, comment, revision=None, | |
|
77 | pull_request=None): | |
|
75 | 78 | """ |
|
76 | 79 | Creates new status for changeset or updates the old ones bumping their |
|
77 | 80 | version, leaving the current status at |
@@ -89,20 +92,48 b' class ChangesetStatusModel(BaseModel):' | |||
|
89 | 92 | """ |
|
90 | 93 | repo = self._get_repo(repo) |
|
91 | 94 | |
|
92 |
|
|
|
93 | .filter(ChangesetStatus.repo == repo)\ | |
|
94 | .filter(ChangesetStatus.revision == revision)\ | |
|
95 | .all() | |
|
95 | q = ChangesetStatus.query() | |
|
96 | ||
|
97 | if revision: | |
|
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 | 106 | if cur_statuses: |
|
97 | 107 | for st in cur_statuses: |
|
98 | 108 | st.version += 1 |
|
99 | 109 | self.sa.add(st) |
|
100 | new_status = ChangesetStatus() | |
|
101 | new_status.author = self._get_user(user) | |
|
102 | new_status.repo = self._get_repo(repo) | |
|
103 |
new_status. |
|
|
104 |
new_status.re |
|
|
105 |
new_status. |
|
|
106 | self.sa.add(new_status) | |
|
107 | return new_status | |
|
110 | ||
|
111 | def _create_status(user, repo, status, comment, revision, pull_request): | |
|
112 | new_status = ChangesetStatus() | |
|
113 | new_status.author = self._get_user(user) | |
|
114 | new_status.repo = self._get_repo(repo) | |
|
115 | new_status.status = status | |
|
116 | new_status.comment = comment | |
|
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 | 55 | user_objects.append(user_obj) |
|
56 | 56 | return user_objects |
|
57 | 57 | |
|
58 |
def create(self, text, repo_id, user_id, revision, |
|
|
59 | line_no=None, status_change=None): | |
|
58 | def create(self, text, repo_id, user_id, revision=None, pull_request=None, | |
|
59 | f_path=None, line_no=None, status_change=None): | |
|
60 | 60 | """ |
|
61 |
Creates new comment for changeset |
|
|
62 |
this comment is associated with a |
|
|
61 | Creates new comment for changeset or pull request. | |
|
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 | 65 | :param text: |
|
65 | 66 | :param repo_id: |
|
66 | 67 | :param user_id: |
|
67 | 68 | :param revision: |
|
69 | :param pull_request: | |
|
68 | 70 | :param f_path: |
|
69 | 71 | :param line_no: |
|
70 | 72 | :param status_change: |
|
71 | 73 | """ |
|
74 | if not text: | |
|
75 | return | |
|
72 | 76 | |
|
73 | if text: | |
|
74 | repo = Repository.get(repo_id) | |
|
77 | 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 | 86 | cs = repo.scm_instance.get_changeset(revision) |
|
76 | 87 | desc = "%s - %s" % (cs.short_id, h.shorter(cs.message, 256)) |
|
77 | 88 | author_email = cs.author_email |
|
78 | comment = ChangesetComment() | |
|
79 | comment.repo = repo | |
|
80 | comment.user_id = user_id | |
|
81 | 89 | comment.revision = revision |
|
82 | comment.text = text | |
|
83 | comment.f_path = f_path | |
|
84 |
comment. |
|
|
90 | elif pull_request: | |
|
91 | pull_request = self.__get_pull_request(pull_request) | |
|
92 | comment.pull_request = pull_request | |
|
93 | desc = '' | |
|
94 | else: | |
|
95 | raise Exception('Please specify revision or pull_request_id') | |
|
85 | 96 | |
|
86 |
|
|
|
87 |
|
|
|
88 | # make notification | |
|
89 | line = '' | |
|
97 | self.sa.add(comment) | |
|
98 | self.sa.flush() | |
|
99 | ||
|
100 | # make notification | |
|
101 | line = '' | |
|
102 | body = text | |
|
103 | ||
|
104 | #changeset | |
|
105 | if revision: | |
|
90 | 106 | if line_no: |
|
91 | 107 | line = _('on line %s') % line_no |
|
92 | 108 | subj = safe_unicode( |
@@ -99,34 +115,41 b' class ChangesetCommentsModel(BaseModel):' | |||
|
99 | 115 | ) |
|
100 | 116 | ) |
|
101 | 117 | ) |
|
102 | ||
|
103 | body = text | |
|
104 | ||
|
118 | notification_type = Notification.TYPE_CHANGESET_COMMENT | |
|
105 | 119 | # get the current participants of this changeset |
|
106 | 120 | recipients = ChangesetComment.get_users(revision=revision) |
|
107 | ||
|
108 | 121 | # add changeset author if it's in rhodecode system |
|
109 | 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 |
|
|
|
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 | 145 | NotificationModel().create( |
|
113 | created_by=user_id, subject=subj, body=body, | |
|
114 | recipients=recipients, type_=Notification.TYPE_CHANGESET_COMMENT, | |
|
115 | email_kwargs={'status_change': status_change} | |
|
146 | created_by=user_id, subject=subj, body=body, | |
|
147 | recipients=mention_recipients, | |
|
148 | type_=notification_type, | |
|
149 | email_kwargs={'status_change': status_change} | |
|
116 | 150 | ) |
|
117 | 151 | |
|
118 | mention_recipients = set(self._extract_mentions(body))\ | |
|
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 | |
|
152 | return comment | |
|
130 | 153 | |
|
131 | 154 | def delete(self, comment): |
|
132 | 155 | """ |
@@ -766,7 +766,12 b' class Repository(Base, BaseModel):' | |||
|
766 | 766 | statuses = statuses.filter(ChangesetStatus.revision.in_(revisions)) |
|
767 | 767 | grouped = {} |
|
768 | 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 | 775 | return grouped |
|
771 | 776 | |
|
772 | 777 | #========================================================================== |
@@ -1336,17 +1341,21 b' class ChangesetComment(Base, BaseModel):' | |||
|
1336 | 1341 | pull_request = relationship('PullRequest', lazy='joined') |
|
1337 | 1342 | |
|
1338 | 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 |
|
|
1346 | Returns user associated with this ChangesetComment. ie those | |
|
1342 | 1347 | who actually commented |
|
1343 | 1348 | |
|
1344 | 1349 | :param cls: |
|
1345 | 1350 | :param revision: |
|
1346 | 1351 | """ |
|
1347 |
|
|
|
1348 | .filter(cls.revision == revision)\ | |
|
1349 | .join(ChangesetComment.author).all() | |
|
1352 | q = Session.query(User)\ | |
|
1353 | .join(ChangesetComment.author) | |
|
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 | 1361 | class ChangesetStatus(Base, BaseModel): |
@@ -1457,6 +1466,7 b' class Notification(Base, BaseModel):' | |||
|
1457 | 1466 | TYPE_MENTION = u'mention' |
|
1458 | 1467 | TYPE_REGISTRATION = u'registration' |
|
1459 | 1468 | TYPE_PULL_REQUEST = u'pull_request' |
|
1469 | TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment' | |
|
1460 | 1470 | |
|
1461 | 1471 | notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True) |
|
1462 | 1472 | subject = Column('subject', Unicode(512), nullable=True) |
@@ -198,13 +198,15 b' class NotificationModel(BaseModel):' | |||
|
198 | 198 | Creates a human readable description based on properties |
|
199 | 199 | of notification object |
|
200 | 200 | """ |
|
201 | ||
|
201 | #alias | |
|
202 | _n = notification | |
|
202 | 203 | _map = { |
|
203 |
|
|
|
204 |
|
|
|
205 |
|
|
|
206 |
|
|
|
207 |
|
|
|
204 | _n.TYPE_CHANGESET_COMMENT: _('commented on commit'), | |
|
205 | _n.TYPE_MESSAGE: _('sent message'), | |
|
206 | _n.TYPE_MENTION: _('mentioned you'), | |
|
207 | _n.TYPE_REGISTRATION: _('registered in RhodeCode'), | |
|
208 | _n.TYPE_PULL_REQUEST: _('opened new pull request'), | |
|
209 | _n.TYPE_PULL_REQUEST_COMMENT: _('commented on pull request') | |
|
208 | 210 | } |
|
209 | 211 | |
|
210 | 212 | tmpl = "%(user)s %(action)s %(when)s" |
@@ -85,7 +85,13 b'' | |||
|
85 | 85 | <div class="changeset-status-container"> |
|
86 | 86 | %if c.statuses.get(cs.raw_id): |
|
87 | 87 | <div title="${_('Changeset status')}" class="changeset-status-lbl">${c.statuses.get(cs.raw_id)[1]}</div> |
|
88 |
<div class="changeset-status-ico"> |
|
|
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 | 95 | %endif |
|
90 | 96 | </div> |
|
91 | 97 | </div> |
@@ -70,8 +70,8 b'' | |||
|
70 | 70 | ##${comment.comment_inline_form(c.changeset)} |
|
71 | 71 | |
|
72 | 72 | ## render comments main comments form and it status |
|
73 |
|
|
|
74 |
|
|
|
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)} | |
|
75 | 75 | |
|
76 | 76 | </div> |
|
77 | 77 |
General Comments 0
You need to be logged in to leave comments.
Login now