Show More
@@ -694,8 +694,8 b' def make_map(config):' | |||
|
694 | 694 | requirements=URL_NAME_REQUIREMENTS, jsroute=True) |
|
695 | 695 | |
|
696 | 696 | rmap.connect('repo_refs_data', '/{repo_name}/refs-data', |
|
697 |
controller='summary', action='repo_refs_data', |
|
|
698 | requirements=URL_NAME_REQUIREMENTS) | |
|
697 | controller='summary', action='repo_refs_data', | |
|
698 | requirements=URL_NAME_REQUIREMENTS, jsroute=True) | |
|
699 | 699 | rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog', |
|
700 | 700 | controller='summary', action='repo_refs_changelog_data', |
|
701 | 701 | requirements=URL_NAME_REQUIREMENTS, jsroute=True) |
@@ -704,9 +704,9 b' def make_map(config):' | |||
|
704 | 704 | jsroute=True, requirements=URL_NAME_REQUIREMENTS) |
|
705 | 705 | |
|
706 | 706 | rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}', |
|
707 |
controller='changeset', revision='tip', |
|
|
707 | controller='changeset', revision='tip', | |
|
708 | 708 | conditions={'function': check_repo}, |
|
709 | requirements=URL_NAME_REQUIREMENTS) | |
|
709 | requirements=URL_NAME_REQUIREMENTS, jsroute=True) | |
|
710 | 710 | rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}', |
|
711 | 711 | controller='changeset', revision='tip', action='changeset_children', |
|
712 | 712 | conditions={'function': check_repo}, |
@@ -923,7 +923,7 b' def make_map(config):' | |||
|
923 | 923 | controller='pullrequests', |
|
924 | 924 | action='show', conditions={'function': check_repo, |
|
925 | 925 | 'method': ['GET']}, |
|
926 | requirements=URL_NAME_REQUIREMENTS) | |
|
926 | requirements=URL_NAME_REQUIREMENTS, jsroute=True) | |
|
927 | 927 | |
|
928 | 928 | rmap.connect('pullrequest_update', |
|
929 | 929 | '/{repo_name}/pull-request/{pull_request_id}', |
@@ -904,6 +904,8 b' class PullrequestsController(BaseRepoCon' | |||
|
904 | 904 | status = request.POST.get('changeset_status', None) |
|
905 | 905 | text = request.POST.get('text') |
|
906 | 906 | comment_type = request.POST.get('comment_type') |
|
907 | resolves_comment_id = request.POST.get('resolves_comment_id') | |
|
908 | ||
|
907 | 909 | if status and '_closed' in status: |
|
908 | 910 | close_pr = True |
|
909 | 911 | status = status.replace('_closed', '') |
@@ -936,7 +938,8 b' class PullrequestsController(BaseRepoCon' | |||
|
936 | 938 | status_change_type=(status |
|
937 | 939 | if status and allowed_to_change_status else None), |
|
938 | 940 | closing_pr=close_pr, |
|
939 | comment_type=comment_type | |
|
941 | comment_type=comment_type, | |
|
942 | resolves_comment_id=resolves_comment_id | |
|
940 | 943 | ) |
|
941 | 944 | |
|
942 | 945 | if allowed_to_change_status: |
@@ -84,9 +84,10 b' class CommentsModel(BaseModel):' | |||
|
84 | 84 | return global_renderer |
|
85 | 85 | |
|
86 | 86 | def create(self, text, repo, user, commit_id=None, pull_request=None, |
|
87 |
f_path=None, line_no=None, status_change=None, |
|
|
88 |
status_change_type=None, c |
|
|
89 | send_email=True, renderer=None): | |
|
87 | f_path=None, line_no=None, status_change=None, | |
|
88 | status_change_type=None, comment_type=None, | |
|
89 | resolves_comment_id=None, closing_pr=False, send_email=True, | |
|
90 | renderer=None): | |
|
90 | 91 | """ |
|
91 | 92 | Creates new comment for commit or pull request. |
|
92 | 93 | IF status_change is not none this comment is associated with a |
@@ -113,6 +114,8 b' class CommentsModel(BaseModel):' | |||
|
113 | 114 | if not renderer: |
|
114 | 115 | renderer = self._get_renderer() |
|
115 | 116 | |
|
117 | repo = self._get_repo(repo) | |
|
118 | user = self._get_user(user) | |
|
116 | 119 | |
|
117 | 120 | schema = comment_schema.CommentSchema() |
|
118 | 121 | validated_kwargs = schema.deserialize(dict( |
@@ -121,15 +124,12 b' class CommentsModel(BaseModel):' | |||
|
121 | 124 | comment_file=f_path, |
|
122 | 125 | comment_line=line_no, |
|
123 | 126 | renderer_type=renderer, |
|
124 | status_change=status_change, | |
|
125 | ||
|
126 | repo=repo, | |
|
127 | user=user, | |
|
127 | status_change=status_change_type, | |
|
128 | resolves_comment_id=resolves_comment_id, | |
|
129 | repo=repo.repo_id, | |
|
130 | user=user.user_id, | |
|
128 | 131 | )) |
|
129 | 132 | |
|
130 | repo = self._get_repo(validated_kwargs['repo']) | |
|
131 | user = self._get_user(validated_kwargs['user']) | |
|
132 | ||
|
133 | 133 | comment = ChangesetComment() |
|
134 | 134 | comment.renderer = validated_kwargs['renderer_type'] |
|
135 | 135 | comment.text = validated_kwargs['comment_body'] |
@@ -139,6 +139,8 b' class CommentsModel(BaseModel):' | |||
|
139 | 139 | |
|
140 | 140 | comment.repo = repo |
|
141 | 141 | comment.author = user |
|
142 | comment.resolved_comment = self.__get_commit_comment( | |
|
143 | validated_kwargs['resolves_comment_id']) | |
|
142 | 144 | |
|
143 | 145 | pull_request_id = pull_request |
|
144 | 146 |
@@ -2917,7 +2917,7 b' class ChangesetComment(Base, BaseModel):' | |||
|
2917 | 2917 | |
|
2918 | 2918 | comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE) |
|
2919 | 2919 | resolved_comment_id = Column('resolved_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=True) |
|
2920 | resolved_comment = relationship('ChangesetComment', remote_side=comment_id) | |
|
2920 | resolved_comment = relationship('ChangesetComment', remote_side=comment_id, backref='resolved_by') | |
|
2921 | 2921 | author = relationship('User', lazy='joined') |
|
2922 | 2922 | repo = relationship('Repository') |
|
2923 | 2923 | status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan") |
@@ -2959,6 +2959,10 b' class ChangesetComment(Base, BaseModel):' | |||
|
2959 | 2959 | """ |
|
2960 | 2960 | return self.outdated and self.pull_request_version_id != version |
|
2961 | 2961 | |
|
2962 | @property | |
|
2963 | def resolved(self): | |
|
2964 | return self.resolved_by[0] if self.resolved_by else None | |
|
2965 | ||
|
2962 | 2966 | def get_index_version(self, versions): |
|
2963 | 2967 | return self.get_index_from_version( |
|
2964 | 2968 | self.pull_request_version_id, versions) |
@@ -53,18 +53,22 b" comment_types = ['note', 'todo']" | |||
|
53 | 53 | |
|
54 | 54 | |
|
55 | 55 | class CommentSchema(colander.MappingSchema): |
|
56 | from rhodecode.model.db import ChangesetComment | |
|
56 | from rhodecode.model.db import ChangesetComment, ChangesetStatus | |
|
57 | 57 | |
|
58 | 58 | comment_body = colander.SchemaNode(colander.String()) |
|
59 | 59 | comment_type = colander.SchemaNode( |
|
60 | 60 | colander.String(), |
|
61 |
validator=colander.OneOf(ChangesetComment.COMMENT_TYPES) |
|
|
61 | validator=colander.OneOf(ChangesetComment.COMMENT_TYPES), | |
|
62 | missing=ChangesetComment.COMMENT_TYPE_NOTE) | |
|
62 | 63 | |
|
63 | 64 | comment_file = colander.SchemaNode(colander.String(), missing=None) |
|
64 | 65 | comment_line = colander.SchemaNode(colander.String(), missing=None) |
|
65 |
status_change = colander.SchemaNode( |
|
|
66 | status_change = colander.SchemaNode( | |
|
67 | colander.String(), missing=None, | |
|
68 | validator=colander.OneOf([x[0] for x in ChangesetStatus.STATUSES])) | |
|
66 | 69 | renderer_type = colander.SchemaNode(colander.String()) |
|
67 | 70 | |
|
68 | # do those ? | |
|
71 | resolves_comment_id = colander.SchemaNode(colander.Integer(), missing=None) | |
|
72 | ||
|
69 | 73 | user = colander.SchemaNode(types.StrOrIntType()) |
|
70 | 74 | repo = colander.SchemaNode(types.StrOrIntType()) |
@@ -66,6 +66,7 b' tr.inline-comments div {' | |||
|
66 | 66 | white-space: nowrap; |
|
67 | 67 | |
|
68 | 68 | text-transform: uppercase; |
|
69 | min-width: 40px; | |
|
69 | 70 | |
|
70 | 71 | &.todo { |
|
71 | 72 | color: @color5; |
@@ -366,7 +367,7 b' form.comment-form {' | |||
|
366 | 367 | display: inline-block; |
|
367 | 368 | } |
|
368 | 369 | |
|
369 |
|
|
|
370 | .comment-button-input { | |
|
370 | 371 | margin-right: 0; |
|
371 | 372 | } |
|
372 | 373 |
@@ -37,6 +37,7 b' function registerRCRoutes() {' | |||
|
37 | 37 | pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']); |
|
38 | 38 | pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']); |
|
39 | 39 | pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']); |
|
40 | pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']); | |
|
40 | 41 | pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']); |
|
41 | 42 | pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']); |
|
42 | 43 | pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']); |
@@ -79,7 +79,7 b' var bindToggleButtons = function() {' | |||
|
79 | 79 | }; |
|
80 | 80 | |
|
81 | 81 | var linkifyComments = function(comments) { |
|
82 |
/* TODO: |
|
|
82 | /* TODO: marcink: remove this - it should no longer needed */ | |
|
83 | 83 | for (var i = 0; i < comments.length; i++) { |
|
84 | 84 | var comment_id = $(comments[i]).data('comment-id'); |
|
85 | 85 | var prev_comment_id = $(comments[i - 1]).data('comment-id'); |
@@ -96,6 +96,8 b' var linkifyComments = function(comments)' | |||
|
96 | 96 | $('#next_c_' + comment_id + " a.arrow_comment_link").attr( |
|
97 | 97 | 'href', '#comment-' + next_comment_id).removeClass('disabled'); |
|
98 | 98 | } |
|
99 | /* TODO(marcink): end removal here */ | |
|
100 | ||
|
99 | 101 | // place a first link to the total counter |
|
100 | 102 | if (i === 0) { |
|
101 | 103 | $('#inline-comments-counter').attr('href', '#comment-' + comment_id); |
@@ -106,10 +108,23 b' var linkifyComments = function(comments)' | |||
|
106 | 108 | |
|
107 | 109 | |
|
108 | 110 | /* Comment form for main and inline comments */ |
|
109 | var CommentForm = (function() { | |
|
111 | ||
|
112 | (function(mod) { | |
|
113 | if (typeof exports == "object" && typeof module == "object") // CommonJS | |
|
114 | module.exports = mod(); | |
|
115 | else // Plain browser env | |
|
116 | (this || window).CommentForm = mod(); | |
|
117 | ||
|
118 | })(function() { | |
|
110 | 119 | "use strict"; |
|
111 | 120 | |
|
112 | function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions) { | |
|
121 | function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId) { | |
|
122 | if (!(this instanceof CommentForm)) { | |
|
123 | return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId); | |
|
124 | } | |
|
125 | ||
|
126 | // bind the element instance to our Form | |
|
127 | $(formElement).get(0).CommentForm = this; | |
|
113 | 128 | |
|
114 | 129 | this.withLineNo = function(selector) { |
|
115 | 130 | var lineNo = this.lineNo; |
@@ -135,6 +150,9 b' var CommentForm = (function() {' | |||
|
135 | 150 | this.cancelButton = this.withLineNo('#cancel-btn'); |
|
136 | 151 | this.commentType = this.withLineNo('#comment_type'); |
|
137 | 152 | |
|
153 | this.resolvesId = null; | |
|
154 | this.resolvesActionId = null; | |
|
155 | ||
|
138 | 156 | this.cmBox = this.withLineNo('#text'); |
|
139 | 157 | this.cm = initCommentBoxCodeMirror(this.cmBox, this.initAutocompleteActions); |
|
140 | 158 | |
@@ -147,17 +165,38 b' var CommentForm = (function() {' | |||
|
147 | 165 | this.previewUrl = pyroutes.url('changeset_comment_preview', |
|
148 | 166 | {'repo_name': templateContext.repo_name}); |
|
149 | 167 | |
|
150 | // based on commitId, or pullReuqestId decide where do we submit | |
|
168 | if (resolvesCommentId){ | |
|
169 | this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId); | |
|
170 | this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId); | |
|
171 | $(this.commentType).prop('disabled', true); | |
|
172 | $(this.commentType).addClass('disabled'); | |
|
173 | ||
|
174 | var resolvedInfo = ( | |
|
175 | '<li class="">' + | |
|
176 | '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' + | |
|
177 | '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' + | |
|
178 | '</li>' | |
|
179 | ).format(resolvesCommentId, _gettext('resolve comment')); | |
|
180 | $(resolvedInfo).insertAfter($(this.commentType).parent()); | |
|
181 | } | |
|
182 | ||
|
183 | // based on commitId, or pullRequestId decide where do we submit | |
|
151 | 184 | // out data |
|
152 | 185 | if (this.commitId){ |
|
153 | 186 | this.submitUrl = pyroutes.url('changeset_comment', |
|
154 | 187 | {'repo_name': templateContext.repo_name, |
|
155 | 188 | 'revision': this.commitId}); |
|
189 | this.selfUrl = pyroutes.url('changeset_home', | |
|
190 | {'repo_name': templateContext.repo_name, | |
|
191 | 'revision': this.commitId}); | |
|
156 | 192 | |
|
157 | 193 | } else if (this.pullRequestId) { |
|
158 | 194 | this.submitUrl = pyroutes.url('pullrequest_comment', |
|
159 | 195 | {'repo_name': templateContext.repo_name, |
|
160 | 196 | 'pull_request_id': this.pullRequestId}); |
|
197 | this.selfUrl = pyroutes.url('pullrequest_show', | |
|
198 | {'repo_name': templateContext.repo_name, | |
|
199 | 'pull_request_id': this.pullRequestId}); | |
|
161 | 200 | |
|
162 | 201 | } else { |
|
163 | 202 | throw new Error( |
@@ -168,6 +207,13 b' var CommentForm = (function() {' | |||
|
168 | 207 | return this.cm |
|
169 | 208 | }; |
|
170 | 209 | |
|
210 | this.setPlaceholder = function(placeholder) { | |
|
211 | var cm = this.getCmInstance(); | |
|
212 | if (cm){ | |
|
213 | cm.setOption('placeholder', placeholder); | |
|
214 | } | |
|
215 | }; | |
|
216 | ||
|
171 | 217 | var self = this; |
|
172 | 218 | |
|
173 | 219 | this.getCommentStatus = function() { |
@@ -176,6 +222,15 b' var CommentForm = (function() {' | |||
|
176 | 222 | this.getCommentType = function() { |
|
177 | 223 | return $(this.submitForm).find(this.commentType).val(); |
|
178 | 224 | }; |
|
225 | ||
|
226 | this.getResolvesId = function() { | |
|
227 | return $(this.submitForm).find(this.resolvesId).val() || null; | |
|
228 | }; | |
|
229 | this.markCommentResolved = function(resolvedCommentId){ | |
|
230 | $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show(); | |
|
231 | $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide(); | |
|
232 | }; | |
|
233 | ||
|
179 | 234 | this.isAllowedToSubmit = function() { |
|
180 | 235 | return !$(this.submitButton).prop('disabled'); |
|
181 | 236 | }; |
@@ -205,12 +260,12 b' var CommentForm = (function() {' | |||
|
205 | 260 | }); |
|
206 | 261 | $(this.submitForm).find(this.statusChange).on('change', function() { |
|
207 | 262 | var status = self.getCommentStatus(); |
|
208 |
if (status && |
|
|
263 | if (status && self.lineNo == 'general') { | |
|
209 | 264 | $(self.submitButton).prop('disabled', false); |
|
210 | 265 | } |
|
211 | //todo, fix this name | |
|
266 | ||
|
212 | 267 | var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status); |
|
213 |
self. |
|
|
268 | self.setPlaceholder(placeholderText) | |
|
214 | 269 | }) |
|
215 | 270 | }; |
|
216 | 271 | |
@@ -260,6 +315,7 b' var CommentForm = (function() {' | |||
|
260 | 315 | var text = self.cm.getValue(); |
|
261 | 316 | var status = self.getCommentStatus(); |
|
262 | 317 | var commentType = self.getCommentType(); |
|
318 | var resolvesCommentId = self.getResolvesId(); | |
|
263 | 319 | |
|
264 | 320 | if (text === "" && !status) { |
|
265 | 321 | return; |
@@ -275,7 +331,9 b' var CommentForm = (function() {' | |||
|
275 | 331 | 'comment_type': commentType, |
|
276 | 332 | 'csrf_token': CSRF_TOKEN |
|
277 | 333 | }; |
|
278 | ||
|
334 | if (resolvesCommentId){ | |
|
335 | postData['resolves_comment_id'] = resolvesCommentId; | |
|
336 | } | |
|
279 | 337 | var submitSuccessCallback = function(o) { |
|
280 | 338 | if (status) { |
|
281 | 339 | location.reload(true); |
@@ -284,10 +342,15 b' var CommentForm = (function() {' | |||
|
284 | 342 | self.resetCommentFormState(); |
|
285 | 343 | bindDeleteCommentButtons(); |
|
286 | 344 | timeagoActivate(); |
|
345 | ||
|
346 | //mark visually which comment was resolved | |
|
347 | if (resolvesCommentId) { | |
|
348 | this.markCommentResolved(resolvesCommentId); | |
|
349 | } | |
|
287 | 350 | } |
|
288 | 351 | }; |
|
289 | 352 | var submitFailCallback = function(){ |
|
290 | self.resetCommentFormState(text) | |
|
353 | self.resetCommentFormState(text); | |
|
291 | 354 | }; |
|
292 | 355 | self.submitAjaxPOST( |
|
293 | 356 | self.submitUrl, postData, submitSuccessCallback, submitFailCallback); |
@@ -367,7 +430,7 b' var CommentForm = (function() {' | |||
|
367 | 430 | |
|
368 | 431 | var postData = { |
|
369 | 432 | 'text': text, |
|
370 | 'renderer': DEFAULT_RENDERER, | |
|
433 | 'renderer': templateContext.visual.default_renderer, | |
|
371 | 434 | 'csrf_token': CSRF_TOKEN |
|
372 | 435 | }; |
|
373 | 436 | |
@@ -404,15 +467,17 b' var CommentForm = (function() {' | |||
|
404 | 467 | } |
|
405 | 468 | |
|
406 | 469 | return CommentForm; |
|
407 |
}) |
|
|
470 | }); | |
|
408 | 471 | |
|
409 | var CommentsController = function() { /* comments controller */ | |
|
472 | /* comments controller */ | |
|
473 | var CommentsController = function() { | |
|
474 | var mainComment = '#text'; | |
|
410 | 475 | var self = this; |
|
411 | 476 | |
|
412 | 477 | this.cancelComment = function(node) { |
|
413 | 478 | var $node = $(node); |
|
414 | 479 | var $td = $node.closest('td'); |
|
415 |
$node.closest('.comment-inline-form').remove |
|
|
480 | $node.closest('.comment-inline-form').remove(); | |
|
416 | 481 | return false; |
|
417 | 482 | }; |
|
418 | 483 | |
@@ -530,7 +595,8 b' var CommentsController = function() { /*' | |||
|
530 | 595 | $node.closest('tr').toggleClass('hide-line-comments'); |
|
531 | 596 | }; |
|
532 | 597 | |
|
533 | this.createComment = function(node) { | |
|
598 | this.createComment = function(node, resolutionComment) { | |
|
599 | var resolvesCommentId = resolutionComment || null; | |
|
534 | 600 | var $node = $(node); |
|
535 | 601 | var $td = $node.closest('td'); |
|
536 | 602 | var $form = $td.find('.comment-inline-form'); |
@@ -556,14 +622,16 b' var CommentsController = function() { /*' | |||
|
556 | 622 | |
|
557 | 623 | var pullRequestId = templateContext.pull_request_data.pull_request_id; |
|
558 | 624 | var commitId = templateContext.commit_data.commit_id; |
|
559 | var _form = $form[0]; | |
|
560 | var commentForm = new CommentForm(_form, commitId, pullRequestId, lineno, false); | |
|
625 | var _form = $($form[0]).find('form'); | |
|
626 | ||
|
627 | var commentForm = new CommentForm(_form, commitId, pullRequestId, lineno, false, resolvesCommentId); | |
|
561 | 628 | var cm = commentForm.getCmInstance(); |
|
562 | 629 | |
|
563 | 630 | // set a CUSTOM submit handler for inline comments. |
|
564 | 631 | commentForm.setHandleFormSubmit(function(o) { |
|
565 | 632 | var text = commentForm.cm.getValue(); |
|
566 | 633 | var commentType = commentForm.getCommentType(); |
|
634 | var resolvesCommentId = commentForm.getResolvesId(); | |
|
567 | 635 | |
|
568 | 636 | if (text === "") { |
|
569 | 637 | return; |
@@ -589,6 +657,10 b' var CommentsController = function() { /*' | |||
|
589 | 657 | 'comment_type': commentType, |
|
590 | 658 | 'csrf_token': CSRF_TOKEN |
|
591 | 659 | }; |
|
660 | if (resolvesCommentId){ | |
|
661 | postData['resolves_comment_id'] = resolvesCommentId; | |
|
662 | } | |
|
663 | ||
|
592 | 664 | var submitSuccessCallback = function(json_data) { |
|
593 | 665 | $form.remove(); |
|
594 | 666 | try { |
@@ -598,6 +670,11 b' var CommentsController = function() { /*' | |||
|
598 | 670 | |
|
599 | 671 | $comments.find('.cb-comment-add-button').before(html); |
|
600 | 672 | |
|
673 | //mark visually which comment was resolved | |
|
674 | if (resolvesCommentId) { | |
|
675 | commentForm.markCommentResolved(resolvesCommentId); | |
|
676 | } | |
|
677 | ||
|
601 | 678 | } catch (e) { |
|
602 | 679 | console.error(e); |
|
603 | 680 | } |
@@ -616,26 +693,81 b' var CommentsController = function() { /*' | |||
|
616 | 693 | commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback); |
|
617 | 694 | }); |
|
618 | 695 | |
|
696 | if (resolvesCommentId){ | |
|
697 | var placeholderText = _gettext('Leave a comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId); | |
|
698 | ||
|
699 | } else { | |
|
700 | var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno); | |
|
701 | } | |
|
702 | ||
|
619 | 703 | setTimeout(function() { |
|
620 | 704 | // callbacks |
|
621 | 705 | if (cm !== undefined) { |
|
622 | cm.setOption('placeholder', _gettext('Leave a comment on line {0}.').format(lineno)); | |
|
706 | commentForm.setPlaceholder(placeholderText); | |
|
623 | 707 | cm.focus(); |
|
624 | 708 | cm.refresh(); |
|
625 | 709 | } |
|
626 | 710 | }, 10); |
|
627 | 711 | |
|
628 |
|
|
|
629 |
|
|
|
630 |
|
|
|
631 |
|
|
|
632 |
|
|
|
633 |
|
|
|
712 | $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({ | |
|
713 | form: _form, | |
|
714 | parent: $td[0], | |
|
715 | lineno: lineno, | |
|
716 | f_path: f_path} | |
|
717 | ); | |
|
718 | ||
|
719 | // trigger hash | |
|
720 | if (resolvesCommentId){ | |
|
721 | var resolveAction = $(commentForm.resolvesActionId); | |
|
722 | setTimeout(function() { | |
|
723 | $('body, html').animate({ scrollTop: resolveAction.offset().top }, 10); | |
|
724 | }, 100); | |
|
725 | } | |
|
634 | 726 | } |
|
635 | 727 | |
|
636 | 728 | $form.addClass('comment-inline-form-open'); |
|
637 | 729 | }; |
|
638 | 730 | |
|
731 | this.createResolutionComment = function(commentId){ | |
|
732 | // hide the trigger text | |
|
733 | $('#resolve-comment-{0}'.format(commentId)).hide(); | |
|
734 | ||
|
735 | var comment = $('#comment-'+commentId); | |
|
736 | var commentData = comment.data(); | |
|
737 | ||
|
738 | if (commentData.commentInline) { | |
|
739 | var resolutionComment = true; | |
|
740 | this.createComment(comment, commentId) | |
|
741 | } else { | |
|
742 | ||
|
743 | this.createComment(comment, commentId) | |
|
744 | ||
|
745 | console.log('TODO') | |
|
746 | console.log(commentId) | |
|
747 | } | |
|
748 | ||
|
749 | return false; | |
|
750 | }; | |
|
751 | ||
|
752 | this.submitResolution = function(commentId){ | |
|
753 | var form = $('#resolve_comment_{0}'.format(commentId)).closest('form'); | |
|
754 | var commentForm = form.get(0).CommentForm; | |
|
755 | ||
|
756 | var cm = commentForm.getCmInstance(); | |
|
757 | var renderer = templateContext.visual.default_renderer; | |
|
758 | if (renderer == 'rst'){ | |
|
759 | var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl); | |
|
760 | } else if (renderer == 'markdown') { | |
|
761 | var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl); | |
|
762 | } else { | |
|
763 | var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl); | |
|
764 | } | |
|
765 | ||
|
766 | cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl)); | |
|
767 | form.submit(); | |
|
768 | return false; | |
|
769 | }; | |
|
770 | ||
|
639 | 771 | this.renderInlineComments = function(file_comments) { |
|
640 | 772 | show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true; |
|
641 | 773 |
@@ -15,12 +15,33 b'' | |||
|
15 | 15 | id="comment-${comment.comment_id}" |
|
16 | 16 | line="${comment.line_no}" |
|
17 | 17 | data-comment-id="${comment.comment_id}" |
|
18 | data-comment-type="${comment.comment_type}" | |
|
19 | data-comment-inline=${h.json.dumps(inline)} | |
|
18 | 20 | style="${'display: none;' if outdated_at_ver else ''}"> |
|
19 | 21 | |
|
20 | 22 | <div class="meta"> |
|
21 |
<div class="comment-type-label |
|
|
22 | <div class="comment-label ${comment.comment_type or 'note'}"> | |
|
23 |
|
|
|
23 | <div class="comment-type-label"> | |
|
24 | <div class="comment-label ${comment.comment_type or 'note'}" id="comment-label-${comment.comment_id}"> | |
|
25 | % if comment.comment_type == 'todo': | |
|
26 | % if comment.resolved: | |
|
27 | <div class="resolved tooltip" title="${_('Resolved by comment #{}').format(comment.resolved.comment_id)}"> | |
|
28 | <a href="#comment-${comment.resolved.comment_id}">${comment.comment_type}</a> | |
|
29 | </div> | |
|
30 | % else: | |
|
31 | <div class="resolved tooltip" style="display: none"> | |
|
32 | <span>${comment.comment_type}</span> | |
|
33 | </div> | |
|
34 | <div class="resolve tooltip" onclick="return Rhodecode.comments.createResolutionComment(${comment.comment_id});" title="${_('Click to resolve this comment')}"> | |
|
35 | ${comment.comment_type} | |
|
36 | </div> | |
|
37 | % endif | |
|
38 | % else: | |
|
39 | % if comment.resolved_comment: | |
|
40 | fix | |
|
41 | % else: | |
|
42 | ${comment.comment_type or 'note'} | |
|
43 | % endif | |
|
44 | % endif | |
|
24 | 45 | </div> |
|
25 | 46 | </div> |
|
26 | 47 | |
@@ -120,6 +141,7 b'' | |||
|
120 | 141 | |
|
121 | 142 | </div> |
|
122 | 143 | </%def> |
|
144 | ||
|
123 | 145 | ## generate main comments |
|
124 | 146 | <%def name="generate_comments(include_pull_request=False, is_pull_request=False)"> |
|
125 | 147 | <div id="comments"> |
@@ -137,16 +159,10 b'' | |||
|
137 | 159 | </div> |
|
138 | 160 | </%def> |
|
139 | 161 | |
|
140 | ## MAIN COMMENT FORM | |
|
162 | ||
|
141 | 163 | <%def name="comments(post_url, cur_status, is_pull_request=False, is_compare=False, change_status=True, form_extras=None)"> |
|
142 | 164 | |
|
143 | %if is_compare: | |
|
144 | <% form_id = "comments_form_compare" %> | |
|
145 | %else: | |
|
146 | <% form_id = "comments_form" %> | |
|
147 | %endif | |
|
148 | ||
|
149 | ||
|
165 | ## merge status, and merge action | |
|
150 | 166 | %if is_pull_request: |
|
151 | 167 | <div class="pull-request-merge"> |
|
152 | 168 | %if c.allowed_to_merge: |
@@ -168,6 +184,7 b'' | |||
|
168 | 184 | %endif |
|
169 | 185 | </div> |
|
170 | 186 | %endif |
|
187 | ||
|
171 | 188 | <div class="comments"> |
|
172 | 189 | <% |
|
173 | 190 | if is_pull_request: |
@@ -177,78 +194,28 b'' | |||
|
177 | 194 | else: |
|
178 | 195 | placeholder = _('Leave a comment on this Commit.') |
|
179 | 196 | %> |
|
197 | ||
|
180 | 198 | % if c.rhodecode_user.username != h.DEFAULT_USER: |
|
181 | 199 | <div class="comment-form ac"> |
|
182 | ${h.secure_form(post_url, id_=form_id)} | |
|
183 | <div class="comment-area"> | |
|
184 | <div class="comment-area-header"> | |
|
185 | <ul class="nav-links clearfix"> | |
|
186 | <li class="active"> | |
|
187 | <a href="#edit-btn" tabindex="-1" id="edit-btn">${_('Write')}</a> | |
|
188 | </li> | |
|
189 | <li class=""> | |
|
190 | <a href="#preview-btn" tabindex="-1" id="preview-btn">${_('Preview')}</a> | |
|
191 | </li> | |
|
192 | <li class="pull-right"> | |
|
193 | <select class="comment-type" id="comment_type" name="comment_type"> | |
|
194 | % for val in c.visual.comment_types: | |
|
195 | <option value="${val}">${val.upper()}</option> | |
|
196 | % endfor | |
|
197 | </select> | |
|
198 | </li> | |
|
199 | </ul> | |
|
200 | </div> | |
|
201 | ||
|
202 | <div class="comment-area-write" style="display: block;"> | |
|
203 | <div id="edit-container"> | |
|
204 | <textarea id="text" name="text" class="comment-block-ta ac-input"></textarea> | |
|
205 | </div> | |
|
206 | <div id="preview-container" class="clearfix" style="display: none;"> | |
|
207 | <div id="preview-box" class="preview-box"></div> | |
|
208 | </div> | |
|
209 | </div> | |
|
200 | ## inject form here | |
|
201 | ${comment_form(form_type='general', form_id='general_comment', lineno_id='general', review_statuses=c.commit_statuses, form_extras=form_extras)} | |
|
202 | </div> | |
|
203 | <script type="text/javascript"> | |
|
204 | // init active elements of commentForm | |
|
205 | var commitId = templateContext.commit_data.commit_id; | |
|
206 | var pullRequestId = templateContext.pull_request_data.pull_request_id; | |
|
207 | var lineNo = 'general'; | |
|
208 | var resolvesCommitId = null; | |
|
210 | 209 | |
|
211 | <div class="comment-area-footer"> | |
|
212 | <div class="toolbar"> | |
|
213 | <div class="toolbar-text"> | |
|
214 | ${(_('Comments parsed using %s syntax with %s support.') % ( | |
|
215 | ('<a href="%s">%s</a>' % (h.url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())), | |
|
216 | ('<span class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user')) | |
|
217 | ) | |
|
218 | )|n} | |
|
219 | </div> | |
|
220 | </div> | |
|
221 | </div> | |
|
222 | </div> | |
|
210 | var mainCommentForm = new CommentForm( | |
|
211 | "#general_comment", commitId, pullRequestId, lineNo, true, resolvesCommitId); | |
|
212 | mainCommentForm.setPlaceholder("${placeholder}"); | |
|
213 | mainCommentForm.initStatusChangeSelector(); | |
|
214 | </script> | |
|
223 | 215 | |
|
224 | <div id="comment_form_extras"> | |
|
225 | %if form_extras and isinstance(form_extras, (list, tuple)): | |
|
226 | % for form_ex_el in form_extras: | |
|
227 | ${form_ex_el|n} | |
|
228 | % endfor | |
|
229 | %endif | |
|
230 | </div> | |
|
231 | <div class="comment-footer"> | |
|
232 | %if change_status: | |
|
233 | <div class="status_box"> | |
|
234 | <select id="change_status" name="changeset_status"> | |
|
235 | <option></option> # Placeholder | |
|
236 | %for status,lbl in c.commit_statuses: | |
|
237 | <option value="${status}" data-status="${status}">${lbl}</option> | |
|
238 | %if is_pull_request and change_status and status in ('approved', 'rejected'): | |
|
239 | <option value="${status}_closed" data-status="${status}">${lbl} & ${_('Closed')}</option> | |
|
240 | %endif | |
|
241 | %endfor | |
|
242 | </select> | |
|
243 | </div> | |
|
244 | %endif | |
|
245 | <div class="action-buttons"> | |
|
246 | <div class="comment-button">${h.submit('save', _('Comment'), class_="btn btn-success comment-button-input")}</div> | |
|
247 | </div> | |
|
248 | </div> | |
|
249 | ${h.end_form()} | |
|
250 | </div> | |
|
216 | ||
|
251 | 217 |
|
|
218 | ## form state when not logged in | |
|
252 | 219 | <div class="comment-form ac"> |
|
253 | 220 | |
|
254 | 221 | <div class="comment-area"> |
@@ -289,22 +256,98 b'' | |||
|
289 | 256 | </div> |
|
290 | 257 | % endif |
|
291 | 258 | |
|
259 | <script type="text/javascript"> | |
|
260 | bindToggleButtons(); | |
|
261 | </script> | |
|
292 | 262 | </div> |
|
263 | </%def> | |
|
264 | ||
|
265 | ||
|
266 | <%def name="comment_form(form_type, form_id='', lineno_id='{1}', review_statuses=None, form_extras=None)"> | |
|
267 | ## comment injected based on assumption that user is logged in | |
|
268 | ||
|
269 | <form ${'id="{}"'.format(form_id) if form_id else '' |n} action="#" method="GET"> | |
|
293 | 270 | |
|
294 | <script> | |
|
295 | // init active elements of commentForm | |
|
296 | var commitId = templateContext.commit_data.commit_id; | |
|
297 | var pullRequestId = templateContext.pull_request_data.pull_request_id; | |
|
298 | var lineNo; | |
|
271 | <div class="comment-area"> | |
|
272 | <div class="comment-area-header"> | |
|
273 | <ul class="nav-links clearfix"> | |
|
274 | <li class="active"> | |
|
275 | <a href="#edit-btn" tabindex="-1" id="edit-btn_${lineno_id}">${_('Write')}</a> | |
|
276 | </li> | |
|
277 | <li class=""> | |
|
278 | <a href="#preview-btn" tabindex="-1" id="preview-btn_${lineno_id}">${_('Preview')}</a> | |
|
279 | </li> | |
|
280 | <li class="pull-right"> | |
|
281 | <select class="comment-type" id="comment_type_${lineno_id}" name="comment_type"> | |
|
282 | % for val in c.visual.comment_types: | |
|
283 | <option value="${val}">${val.upper()}</option> | |
|
284 | % endfor | |
|
285 | </select> | |
|
286 | </li> | |
|
287 | </ul> | |
|
288 | </div> | |
|
289 | ||
|
290 | <div class="comment-area-write" style="display: block;"> | |
|
291 | <div id="edit-container_${lineno_id}"> | |
|
292 | <textarea id="text_${lineno_id}" name="text" class="comment-block-ta ac-input"></textarea> | |
|
293 | </div> | |
|
294 | <div id="preview-container_${lineno_id}" class="clearfix" style="display: none;"> | |
|
295 | <div id="preview-box_${lineno_id}" class="preview-box"></div> | |
|
296 | </div> | |
|
297 | </div> | |
|
299 | 298 | |
|
300 | var mainCommentForm = new CommentForm( | |
|
301 | "#${form_id}", commitId, pullRequestId, lineNo, true); | |
|
299 | <div class="comment-area-footer"> | |
|
300 | <div class="toolbar"> | |
|
301 | <div class="toolbar-text"> | |
|
302 | ${(_('Comments parsed using %s syntax with %s support.') % ( | |
|
303 | ('<a href="%s">%s</a>' % (h.url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())), | |
|
304 | ('<span class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user')) | |
|
305 | ) | |
|
306 | )|n} | |
|
307 | </div> | |
|
308 | </div> | |
|
309 | </div> | |
|
310 | </div> | |
|
311 | ||
|
312 | <div class="comment-footer"> | |
|
302 | 313 | |
|
303 | if (mainCommentForm.cm){ | |
|
304 | mainCommentForm.cm.setOption('placeholder', "${placeholder}"); | |
|
305 | } | |
|
314 | % if review_statuses: | |
|
315 | <div class="status_box"> | |
|
316 | <select id="change_status" name="changeset_status"> | |
|
317 | <option></option> ## Placeholder | |
|
318 | % for status, lbl in review_statuses: | |
|
319 | <option value="${status}" data-status="${status}">${lbl}</option> | |
|
320 | %if is_pull_request and change_status and status in ('approved', 'rejected'): | |
|
321 | <option value="${status}_closed" data-status="${status}">${lbl} & ${_('Closed')}</option> | |
|
322 | %endif | |
|
323 | % endfor | |
|
324 | </select> | |
|
325 | </div> | |
|
326 | % endif | |
|
306 | 327 | |
|
307 | mainCommentForm.initStatusChangeSelector(); | |
|
308 | bindToggleButtons(); | |
|
309 | </script> | |
|
310 | </%def> | |
|
328 | ## inject extra inputs into the form | |
|
329 | % if form_extras and isinstance(form_extras, (list, tuple)): | |
|
330 | <div id="comment_form_extras"> | |
|
331 | % for form_ex_el in form_extras: | |
|
332 | ${form_ex_el|n} | |
|
333 | % endfor | |
|
334 | </div> | |
|
335 | % endif | |
|
336 | ||
|
337 | <div class="action-buttons"> | |
|
338 | ## inline for has a file, and line-number together with cancel hide button. | |
|
339 | % if form_type == 'inline': | |
|
340 | <input type="hidden" name="f_path" value="{0}"> | |
|
341 | <input type="hidden" name="line" value="${lineno_id}"> | |
|
342 | <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);"> | |
|
343 | ${_('Cancel')} | |
|
344 | </button> | |
|
345 | % endif | |
|
346 | ${h.submit('save', _('Comment'), class_='btn btn-success comment-button-input')} | |
|
347 | ||
|
348 | </div> | |
|
349 | </div> | |
|
350 | ||
|
351 | </form> | |
|
352 | ||
|
353 | </%def> No newline at end of file |
@@ -1,3 +1,5 b'' | |||
|
1 | <%namespace name="commentblock" file="/changeset/changeset_file_comment.mako"/> | |
|
2 | ||
|
1 | 3 | <%def name="diff_line_anchor(filename, line, type)"><% |
|
2 | 4 | return '%s_%s_%i' % (h.safeid(filename), type, line) |
|
3 | 5 | %></%def> |
@@ -60,59 +62,8 b" return h.url('', **new_args)" | |||
|
60 | 62 | <div class="comment-inline-form ac"> |
|
61 | 63 | |
|
62 | 64 | %if c.rhodecode_user.username != h.DEFAULT_USER: |
|
63 | ${h.form('#', method='get')} | |
|
64 | <div class="comment-area"> | |
|
65 | <div class="comment-area-header"> | |
|
66 | <ul class="nav-links clearfix"> | |
|
67 | <li class="active"> | |
|
68 | <a href="#edit-btn" tabindex="-1" id="edit-btn_{1}">${_('Write')}</a> | |
|
69 | </li> | |
|
70 | <li class=""> | |
|
71 | <a href="#preview-btn" tabindex="-1" id="preview-btn_{1}">${_('Preview')}</a> | |
|
72 | </li> | |
|
73 | <li class="pull-right"> | |
|
74 | <select class="comment-type" id="comment_type_{1}" name="comment_type"> | |
|
75 | % for val in c.visual.comment_types: | |
|
76 | <option value="${val}">${val.upper()}</option> | |
|
77 | % endfor | |
|
78 | </select> | |
|
79 | </li> | |
|
80 | </ul> | |
|
81 | </div> | |
|
82 | ||
|
83 | <div class="comment-area-write" style="display: block;"> | |
|
84 | <div id="edit-container_{1}"> | |
|
85 | <textarea id="text_{1}" name="text" class="comment-block-ta ac-input"></textarea> | |
|
86 | </div> | |
|
87 | <div id="preview-container_{1}" class="clearfix" style="display: none;"> | |
|
88 | <div id="preview-box_{1}" class="preview-box"></div> | |
|
89 | </div> | |
|
90 | </div> | |
|
91 | ||
|
92 | <div class="comment-area-footer"> | |
|
93 | <div class="toolbar"> | |
|
94 | <div class="toolbar-text"> | |
|
95 | ${(_('Comments parsed using %s syntax with %s support.') % ( | |
|
96 | ('<a href="%s">%s</a>' % (h.url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())), | |
|
97 | ('<span class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user')) | |
|
98 | ) | |
|
99 | )|n} | |
|
100 | </div> | |
|
101 | </div> | |
|
102 | </div> | |
|
103 | </div> | |
|
104 | ||
|
105 | <div class="comment-footer"> | |
|
106 | <div class="action-buttons"> | |
|
107 | <input type="hidden" name="f_path" value="{0}"> | |
|
108 | <input type="hidden" name="line" value="{1}"> | |
|
109 | <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);"> | |
|
110 | ${_('Cancel')} | |
|
111 | </button> | |
|
112 | ${h.submit('save', _('Comment'), class_='btn btn-success save-inline-form')} | |
|
113 | </div> | |
|
114 | ${h.end_form()} | |
|
115 | </div> | |
|
65 | ## render template for inline comments | |
|
66 | ${commentblock.comment_form(form_type='inline')} | |
|
116 | 67 |
|
|
117 | 68 | ${h.form('', class_='inline-form comment-form-login', method='get')} |
|
118 | 69 | <div class="pull-left"> |
@@ -521,7 +472,6 b' from rhodecode.lib.diffs import NEW_FILE' | |||
|
521 | 472 | </%def> |
|
522 | 473 | |
|
523 | 474 | |
|
524 | <%namespace name="commentblock" file="/changeset/changeset_file_comment.mako"/> | |
|
525 | 475 | <%def name="inline_comments_container(comments)"> |
|
526 | 476 | <div class="inline-comments"> |
|
527 | 477 | %for comment in comments: |
General Comments 0
You need to be logged in to leave comments.
Login now