##// END OF EJS Templates
comments: allow submitting id of comment which submitted comment resolved....
marcink -
r1325:b4535cc7 default
parent child Browse files
Show More
@@ -694,8 +694,8 b' def make_map(config):'
694 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
694 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
695
695
696 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
696 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
697 controller='summary', action='repo_refs_data', jsroute=True,
697 controller='summary', action='repo_refs_data',
698 requirements=URL_NAME_REQUIREMENTS)
698 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
699 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
699 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
700 controller='summary', action='repo_refs_changelog_data',
700 controller='summary', action='repo_refs_changelog_data',
701 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
701 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
@@ -704,9 +704,9 b' def make_map(config):'
704 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
704 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
705
705
706 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
706 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
707 controller='changeset', revision='tip', jsroute=True,
707 controller='changeset', revision='tip',
708 conditions={'function': check_repo},
708 conditions={'function': check_repo},
709 requirements=URL_NAME_REQUIREMENTS)
709 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
710 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
710 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
711 controller='changeset', revision='tip', action='changeset_children',
711 controller='changeset', revision='tip', action='changeset_children',
712 conditions={'function': check_repo},
712 conditions={'function': check_repo},
@@ -923,7 +923,7 b' def make_map(config):'
923 controller='pullrequests',
923 controller='pullrequests',
924 action='show', conditions={'function': check_repo,
924 action='show', conditions={'function': check_repo,
925 'method': ['GET']},
925 'method': ['GET']},
926 requirements=URL_NAME_REQUIREMENTS)
926 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
927
927
928 rmap.connect('pullrequest_update',
928 rmap.connect('pullrequest_update',
929 '/{repo_name}/pull-request/{pull_request_id}',
929 '/{repo_name}/pull-request/{pull_request_id}',
@@ -904,6 +904,8 b' class PullrequestsController(BaseRepoCon'
904 status = request.POST.get('changeset_status', None)
904 status = request.POST.get('changeset_status', None)
905 text = request.POST.get('text')
905 text = request.POST.get('text')
906 comment_type = request.POST.get('comment_type')
906 comment_type = request.POST.get('comment_type')
907 resolves_comment_id = request.POST.get('resolves_comment_id')
908
907 if status and '_closed' in status:
909 if status and '_closed' in status:
908 close_pr = True
910 close_pr = True
909 status = status.replace('_closed', '')
911 status = status.replace('_closed', '')
@@ -936,7 +938,8 b' class PullrequestsController(BaseRepoCon'
936 status_change_type=(status
938 status_change_type=(status
937 if status and allowed_to_change_status else None),
939 if status and allowed_to_change_status else None),
938 closing_pr=close_pr,
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 if allowed_to_change_status:
945 if allowed_to_change_status:
@@ -84,9 +84,10 b' class CommentsModel(BaseModel):'
84 return global_renderer
84 return global_renderer
85
85
86 def create(self, text, repo, user, commit_id=None, pull_request=None,
86 def create(self, text, repo, user, commit_id=None, pull_request=None,
87 f_path=None, line_no=None, status_change=None, comment_type=None,
87 f_path=None, line_no=None, status_change=None,
88 status_change_type=None, closing_pr=False,
88 status_change_type=None, comment_type=None,
89 send_email=True, renderer=None):
89 resolves_comment_id=None, closing_pr=False, send_email=True,
90 renderer=None):
90 """
91 """
91 Creates new comment for commit or pull request.
92 Creates new comment for commit or pull request.
92 IF status_change is not none this comment is associated with a
93 IF status_change is not none this comment is associated with a
@@ -113,6 +114,8 b' class CommentsModel(BaseModel):'
113 if not renderer:
114 if not renderer:
114 renderer = self._get_renderer()
115 renderer = self._get_renderer()
115
116
117 repo = self._get_repo(repo)
118 user = self._get_user(user)
116
119
117 schema = comment_schema.CommentSchema()
120 schema = comment_schema.CommentSchema()
118 validated_kwargs = schema.deserialize(dict(
121 validated_kwargs = schema.deserialize(dict(
@@ -121,15 +124,12 b' class CommentsModel(BaseModel):'
121 comment_file=f_path,
124 comment_file=f_path,
122 comment_line=line_no,
125 comment_line=line_no,
123 renderer_type=renderer,
126 renderer_type=renderer,
124 status_change=status_change,
127 status_change=status_change_type,
125
128 resolves_comment_id=resolves_comment_id,
126 repo=repo,
129 repo=repo.repo_id,
127 user=user,
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 comment = ChangesetComment()
133 comment = ChangesetComment()
134 comment.renderer = validated_kwargs['renderer_type']
134 comment.renderer = validated_kwargs['renderer_type']
135 comment.text = validated_kwargs['comment_body']
135 comment.text = validated_kwargs['comment_body']
@@ -139,6 +139,8 b' class CommentsModel(BaseModel):'
139
139
140 comment.repo = repo
140 comment.repo = repo
141 comment.author = user
141 comment.author = user
142 comment.resolved_comment = self.__get_commit_comment(
143 validated_kwargs['resolves_comment_id'])
142
144
143 pull_request_id = pull_request
145 pull_request_id = pull_request
144
146
@@ -2917,7 +2917,7 b' class ChangesetComment(Base, BaseModel):'
2917
2917
2918 comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE)
2918 comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE)
2919 resolved_comment_id = Column('resolved_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=True)
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 author = relationship('User', lazy='joined')
2921 author = relationship('User', lazy='joined')
2922 repo = relationship('Repository')
2922 repo = relationship('Repository')
2923 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
2923 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
@@ -2959,6 +2959,10 b' class ChangesetComment(Base, BaseModel):'
2959 """
2959 """
2960 return self.outdated and self.pull_request_version_id != version
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 def get_index_version(self, versions):
2966 def get_index_version(self, versions):
2963 return self.get_index_from_version(
2967 return self.get_index_from_version(
2964 self.pull_request_version_id, versions)
2968 self.pull_request_version_id, versions)
@@ -53,18 +53,22 b" comment_types = ['note', 'todo']"
53
53
54
54
55 class CommentSchema(colander.MappingSchema):
55 class CommentSchema(colander.MappingSchema):
56 from rhodecode.model.db import ChangesetComment
56 from rhodecode.model.db import ChangesetComment, ChangesetStatus
57
57
58 comment_body = colander.SchemaNode(colander.String())
58 comment_body = colander.SchemaNode(colander.String())
59 comment_type = colander.SchemaNode(
59 comment_type = colander.SchemaNode(
60 colander.String(),
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 comment_file = colander.SchemaNode(colander.String(), missing=None)
64 comment_file = colander.SchemaNode(colander.String(), missing=None)
64 comment_line = colander.SchemaNode(colander.String(), missing=None)
65 comment_line = colander.SchemaNode(colander.String(), missing=None)
65 status_change = colander.SchemaNode(colander.String(), missing=None)
66 status_change = colander.SchemaNode(
67 colander.String(), missing=None,
68 validator=colander.OneOf([x[0] for x in ChangesetStatus.STATUSES]))
66 renderer_type = colander.SchemaNode(colander.String())
69 renderer_type = colander.SchemaNode(colander.String())
67
70
68 # do those ?
71 resolves_comment_id = colander.SchemaNode(colander.Integer(), missing=None)
72
69 user = colander.SchemaNode(types.StrOrIntType())
73 user = colander.SchemaNode(types.StrOrIntType())
70 repo = colander.SchemaNode(types.StrOrIntType())
74 repo = colander.SchemaNode(types.StrOrIntType())
@@ -66,6 +66,7 b' tr.inline-comments div {'
66 white-space: nowrap;
66 white-space: nowrap;
67
67
68 text-transform: uppercase;
68 text-transform: uppercase;
69 min-width: 40px;
69
70
70 &.todo {
71 &.todo {
71 color: @color5;
72 color: @color5;
@@ -366,7 +367,7 b' form.comment-form {'
366 display: inline-block;
367 display: inline-block;
367 }
368 }
368
369
369 .comment-button .comment-button-input {
370 .comment-button-input {
370 margin-right: 0;
371 margin-right: 0;
371 }
372 }
372
373
@@ -37,6 +37,7 b' function registerRCRoutes() {'
37 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
37 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
38 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
38 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
39 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
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 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
41 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
41 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
42 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
42 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
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 var linkifyComments = function(comments) {
81 var linkifyComments = function(comments) {
82 /* TODO: dan: remove this - it should no longer needed */
82 /* TODO: marcink: remove this - it should no longer needed */
83 for (var i = 0; i < comments.length; i++) {
83 for (var i = 0; i < comments.length; i++) {
84 var comment_id = $(comments[i]).data('comment-id');
84 var comment_id = $(comments[i]).data('comment-id');
85 var prev_comment_id = $(comments[i - 1]).data('comment-id');
85 var prev_comment_id = $(comments[i - 1]).data('comment-id');
@@ -96,6 +96,8 b' var linkifyComments = function(comments)'
96 $('#next_c_' + comment_id + " a.arrow_comment_link").attr(
96 $('#next_c_' + comment_id + " a.arrow_comment_link").attr(
97 'href', '#comment-' + next_comment_id).removeClass('disabled');
97 'href', '#comment-' + next_comment_id).removeClass('disabled');
98 }
98 }
99 /* TODO(marcink): end removal here */
100
99 // place a first link to the total counter
101 // place a first link to the total counter
100 if (i === 0) {
102 if (i === 0) {
101 $('#inline-comments-counter').attr('href', '#comment-' + comment_id);
103 $('#inline-comments-counter').attr('href', '#comment-' + comment_id);
@@ -106,10 +108,23 b' var linkifyComments = function(comments)'
106
108
107
109
108 /* Comment form for main and inline comments */
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 "use strict";
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 this.withLineNo = function(selector) {
129 this.withLineNo = function(selector) {
115 var lineNo = this.lineNo;
130 var lineNo = this.lineNo;
@@ -135,6 +150,9 b' var CommentForm = (function() {'
135 this.cancelButton = this.withLineNo('#cancel-btn');
150 this.cancelButton = this.withLineNo('#cancel-btn');
136 this.commentType = this.withLineNo('#comment_type');
151 this.commentType = this.withLineNo('#comment_type');
137
152
153 this.resolvesId = null;
154 this.resolvesActionId = null;
155
138 this.cmBox = this.withLineNo('#text');
156 this.cmBox = this.withLineNo('#text');
139 this.cm = initCommentBoxCodeMirror(this.cmBox, this.initAutocompleteActions);
157 this.cm = initCommentBoxCodeMirror(this.cmBox, this.initAutocompleteActions);
140
158
@@ -147,17 +165,38 b' var CommentForm = (function() {'
147 this.previewUrl = pyroutes.url('changeset_comment_preview',
165 this.previewUrl = pyroutes.url('changeset_comment_preview',
148 {'repo_name': templateContext.repo_name});
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 // out data
184 // out data
152 if (this.commitId){
185 if (this.commitId){
153 this.submitUrl = pyroutes.url('changeset_comment',
186 this.submitUrl = pyroutes.url('changeset_comment',
154 {'repo_name': templateContext.repo_name,
187 {'repo_name': templateContext.repo_name,
155 'revision': this.commitId});
188 'revision': this.commitId});
189 this.selfUrl = pyroutes.url('changeset_home',
190 {'repo_name': templateContext.repo_name,
191 'revision': this.commitId});
156
192
157 } else if (this.pullRequestId) {
193 } else if (this.pullRequestId) {
158 this.submitUrl = pyroutes.url('pullrequest_comment',
194 this.submitUrl = pyroutes.url('pullrequest_comment',
159 {'repo_name': templateContext.repo_name,
195 {'repo_name': templateContext.repo_name,
160 'pull_request_id': this.pullRequestId});
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 } else {
201 } else {
163 throw new Error(
202 throw new Error(
@@ -168,6 +207,13 b' var CommentForm = (function() {'
168 return this.cm
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 var self = this;
217 var self = this;
172
218
173 this.getCommentStatus = function() {
219 this.getCommentStatus = function() {
@@ -176,6 +222,15 b' var CommentForm = (function() {'
176 this.getCommentType = function() {
222 this.getCommentType = function() {
177 return $(this.submitForm).find(this.commentType).val();
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 this.isAllowedToSubmit = function() {
234 this.isAllowedToSubmit = function() {
180 return !$(this.submitButton).prop('disabled');
235 return !$(this.submitButton).prop('disabled');
181 };
236 };
@@ -205,12 +260,12 b' var CommentForm = (function() {'
205 });
260 });
206 $(this.submitForm).find(this.statusChange).on('change', function() {
261 $(this.submitForm).find(this.statusChange).on('change', function() {
207 var status = self.getCommentStatus();
262 var status = self.getCommentStatus();
208 if (status && !self.lineNo) {
263 if (status && self.lineNo == 'general') {
209 $(self.submitButton).prop('disabled', false);
264 $(self.submitButton).prop('disabled', false);
210 }
265 }
211 //todo, fix this name
266
212 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
267 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
213 self.cm.setOption('placeholder', placeholderText);
268 self.setPlaceholder(placeholderText)
214 })
269 })
215 };
270 };
216
271
@@ -260,6 +315,7 b' var CommentForm = (function() {'
260 var text = self.cm.getValue();
315 var text = self.cm.getValue();
261 var status = self.getCommentStatus();
316 var status = self.getCommentStatus();
262 var commentType = self.getCommentType();
317 var commentType = self.getCommentType();
318 var resolvesCommentId = self.getResolvesId();
263
319
264 if (text === "" && !status) {
320 if (text === "" && !status) {
265 return;
321 return;
@@ -275,7 +331,9 b' var CommentForm = (function() {'
275 'comment_type': commentType,
331 'comment_type': commentType,
276 'csrf_token': CSRF_TOKEN
332 'csrf_token': CSRF_TOKEN
277 };
333 };
278
334 if (resolvesCommentId){
335 postData['resolves_comment_id'] = resolvesCommentId;
336 }
279 var submitSuccessCallback = function(o) {
337 var submitSuccessCallback = function(o) {
280 if (status) {
338 if (status) {
281 location.reload(true);
339 location.reload(true);
@@ -284,10 +342,15 b' var CommentForm = (function() {'
284 self.resetCommentFormState();
342 self.resetCommentFormState();
285 bindDeleteCommentButtons();
343 bindDeleteCommentButtons();
286 timeagoActivate();
344 timeagoActivate();
345
346 //mark visually which comment was resolved
347 if (resolvesCommentId) {
348 this.markCommentResolved(resolvesCommentId);
349 }
287 }
350 }
288 };
351 };
289 var submitFailCallback = function(){
352 var submitFailCallback = function(){
290 self.resetCommentFormState(text)
353 self.resetCommentFormState(text);
291 };
354 };
292 self.submitAjaxPOST(
355 self.submitAjaxPOST(
293 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
356 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
@@ -367,7 +430,7 b' var CommentForm = (function() {'
367
430
368 var postData = {
431 var postData = {
369 'text': text,
432 'text': text,
370 'renderer': DEFAULT_RENDERER,
433 'renderer': templateContext.visual.default_renderer,
371 'csrf_token': CSRF_TOKEN
434 'csrf_token': CSRF_TOKEN
372 };
435 };
373
436
@@ -404,15 +467,17 b' var CommentForm = (function() {'
404 }
467 }
405
468
406 return CommentForm;
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 var self = this;
475 var self = this;
411
476
412 this.cancelComment = function(node) {
477 this.cancelComment = function(node) {
413 var $node = $(node);
478 var $node = $(node);
414 var $td = $node.closest('td');
479 var $td = $node.closest('td');
415 $node.closest('.comment-inline-form').removeClass('comment-inline-form-open');
480 $node.closest('.comment-inline-form').remove();
416 return false;
481 return false;
417 };
482 };
418
483
@@ -530,7 +595,8 b' var CommentsController = function() { /*'
530 $node.closest('tr').toggleClass('hide-line-comments');
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 var $node = $(node);
600 var $node = $(node);
535 var $td = $node.closest('td');
601 var $td = $node.closest('td');
536 var $form = $td.find('.comment-inline-form');
602 var $form = $td.find('.comment-inline-form');
@@ -556,14 +622,16 b' var CommentsController = function() { /*'
556
622
557 var pullRequestId = templateContext.pull_request_data.pull_request_id;
623 var pullRequestId = templateContext.pull_request_data.pull_request_id;
558 var commitId = templateContext.commit_data.commit_id;
624 var commitId = templateContext.commit_data.commit_id;
559 var _form = $form[0];
625 var _form = $($form[0]).find('form');
560 var commentForm = new CommentForm(_form, commitId, pullRequestId, lineno, false);
626
627 var commentForm = new CommentForm(_form, commitId, pullRequestId, lineno, false, resolvesCommentId);
561 var cm = commentForm.getCmInstance();
628 var cm = commentForm.getCmInstance();
562
629
563 // set a CUSTOM submit handler for inline comments.
630 // set a CUSTOM submit handler for inline comments.
564 commentForm.setHandleFormSubmit(function(o) {
631 commentForm.setHandleFormSubmit(function(o) {
565 var text = commentForm.cm.getValue();
632 var text = commentForm.cm.getValue();
566 var commentType = commentForm.getCommentType();
633 var commentType = commentForm.getCommentType();
634 var resolvesCommentId = commentForm.getResolvesId();
567
635
568 if (text === "") {
636 if (text === "") {
569 return;
637 return;
@@ -589,6 +657,10 b' var CommentsController = function() { /*'
589 'comment_type': commentType,
657 'comment_type': commentType,
590 'csrf_token': CSRF_TOKEN
658 'csrf_token': CSRF_TOKEN
591 };
659 };
660 if (resolvesCommentId){
661 postData['resolves_comment_id'] = resolvesCommentId;
662 }
663
592 var submitSuccessCallback = function(json_data) {
664 var submitSuccessCallback = function(json_data) {
593 $form.remove();
665 $form.remove();
594 try {
666 try {
@@ -598,6 +670,11 b' var CommentsController = function() { /*'
598
670
599 $comments.find('.cb-comment-add-button').before(html);
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 } catch (e) {
678 } catch (e) {
602 console.error(e);
679 console.error(e);
603 }
680 }
@@ -616,26 +693,81 b' var CommentsController = function() { /*'
616 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
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 setTimeout(function() {
703 setTimeout(function() {
620 // callbacks
704 // callbacks
621 if (cm !== undefined) {
705 if (cm !== undefined) {
622 cm.setOption('placeholder', _gettext('Leave a comment on line {0}.').format(lineno));
706 commentForm.setPlaceholder(placeholderText);
623 cm.focus();
707 cm.focus();
624 cm.refresh();
708 cm.refresh();
625 }
709 }
626 }, 10);
710 }, 10);
627
711
628 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
712 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
629 form: _form,
713 form: _form,
630 parent: $td[0],
714 parent: $td[0],
631 lineno: lineno,
715 lineno: lineno,
632 f_path: f_path}
716 f_path: f_path}
633 );
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 $form.addClass('comment-inline-form-open');
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 this.renderInlineComments = function(file_comments) {
771 this.renderInlineComments = function(file_comments) {
640 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
772 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
641
773
@@ -15,12 +15,33 b''
15 id="comment-${comment.comment_id}"
15 id="comment-${comment.comment_id}"
16 line="${comment.line_no}"
16 line="${comment.line_no}"
17 data-comment-id="${comment.comment_id}"
17 data-comment-id="${comment.comment_id}"
18 data-comment-type="${comment.comment_type}"
19 data-comment-inline=${h.json.dumps(inline)}
18 style="${'display: none;' if outdated_at_ver else ''}">
20 style="${'display: none;' if outdated_at_ver else ''}">
19
21
20 <div class="meta">
22 <div class="meta">
21 <div class="comment-type-label tooltip">
23 <div class="comment-type-label">
22 <div class="comment-label ${comment.comment_type or 'note'}">
24 <div class="comment-label ${comment.comment_type or 'note'}" id="comment-label-${comment.comment_id}">
23 ${comment.comment_type or 'note'}
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 </div>
45 </div>
25 </div>
46 </div>
26
47
@@ -120,6 +141,7 b''
120
141
121 </div>
142 </div>
122 </%def>
143 </%def>
144
123 ## generate main comments
145 ## generate main comments
124 <%def name="generate_comments(include_pull_request=False, is_pull_request=False)">
146 <%def name="generate_comments(include_pull_request=False, is_pull_request=False)">
125 <div id="comments">
147 <div id="comments">
@@ -137,16 +159,10 b''
137 </div>
159 </div>
138 </%def>
160 </%def>
139
161
140 ## MAIN COMMENT FORM
162
141 <%def name="comments(post_url, cur_status, is_pull_request=False, is_compare=False, change_status=True, form_extras=None)">
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:
165 ## merge status, and merge action
144 <% form_id = "comments_form_compare" %>
145 %else:
146 <% form_id = "comments_form" %>
147 %endif
148
149
150 %if is_pull_request:
166 %if is_pull_request:
151 <div class="pull-request-merge">
167 <div class="pull-request-merge">
152 %if c.allowed_to_merge:
168 %if c.allowed_to_merge:
@@ -168,6 +184,7 b''
168 %endif
184 %endif
169 </div>
185 </div>
170 %endif
186 %endif
187
171 <div class="comments">
188 <div class="comments">
172 <%
189 <%
173 if is_pull_request:
190 if is_pull_request:
@@ -177,78 +194,28 b''
177 else:
194 else:
178 placeholder = _('Leave a comment on this Commit.')
195 placeholder = _('Leave a comment on this Commit.')
179 %>
196 %>
197
180 % if c.rhodecode_user.username != h.DEFAULT_USER:
198 % if c.rhodecode_user.username != h.DEFAULT_USER:
181 <div class="comment-form ac">
199 <div class="comment-form ac">
182 ${h.secure_form(post_url, id_=form_id)}
200 ## inject form here
183 <div class="comment-area">
201 ${comment_form(form_type='general', form_id='general_comment', lineno_id='general', review_statuses=c.commit_statuses, form_extras=form_extras)}
184 <div class="comment-area-header">
202 </div>
185 <ul class="nav-links clearfix">
203 <script type="text/javascript">
186 <li class="active">
204 // init active elements of commentForm
187 <a href="#edit-btn" tabindex="-1" id="edit-btn">${_('Write')}</a>
205 var commitId = templateContext.commit_data.commit_id;
188 </li>
206 var pullRequestId = templateContext.pull_request_data.pull_request_id;
189 <li class="">
207 var lineNo = 'general';
190 <a href="#preview-btn" tabindex="-1" id="preview-btn">${_('Preview')}</a>
208 var resolvesCommitId = null;
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>
210
209
211 <div class="comment-area-footer">
210 var mainCommentForm = new CommentForm(
212 <div class="toolbar">
211 "#general_comment", commitId, pullRequestId, lineNo, true, resolvesCommitId);
213 <div class="toolbar-text">
212 mainCommentForm.setPlaceholder("${placeholder}");
214 ${(_('Comments parsed using %s syntax with %s support.') % (
213 mainCommentForm.initStatusChangeSelector();
215 ('<a href="%s">%s</a>' % (h.url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
214 </script>
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>
223
215
224 <div id="comment_form_extras">
216
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>
251 % else:
217 % else:
218 ## form state when not logged in
252 <div class="comment-form ac">
219 <div class="comment-form ac">
253
220
254 <div class="comment-area">
221 <div class="comment-area">
@@ -289,22 +256,98 b''
289 </div>
256 </div>
290 % endif
257 % endif
291
258
259 <script type="text/javascript">
260 bindToggleButtons();
261 </script>
292 </div>
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>
271 <div class="comment-area">
295 // init active elements of commentForm
272 <div class="comment-area-header">
296 var commitId = templateContext.commit_data.commit_id;
273 <ul class="nav-links clearfix">
297 var pullRequestId = templateContext.pull_request_data.pull_request_id;
274 <li class="active">
298 var lineNo;
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(
299 <div class="comment-area-footer">
301 "#${form_id}", commitId, pullRequestId, lineNo, true);
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){
314 % if review_statuses:
304 mainCommentForm.cm.setOption('placeholder', "${placeholder}");
315 <div class="status_box">
305 }
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();
328 ## inject extra inputs into the form
308 bindToggleButtons();
329 % if form_extras and isinstance(form_extras, (list, tuple)):
309 </script>
330 <div id="comment_form_extras">
310 </%def>
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 <%def name="diff_line_anchor(filename, line, type)"><%
3 <%def name="diff_line_anchor(filename, line, type)"><%
2 return '%s_%s_%i' % (h.safeid(filename), type, line)
4 return '%s_%s_%i' % (h.safeid(filename), type, line)
3 %></%def>
5 %></%def>
@@ -60,59 +62,8 b" return h.url('', **new_args)"
60 <div class="comment-inline-form ac">
62 <div class="comment-inline-form ac">
61
63
62 %if c.rhodecode_user.username != h.DEFAULT_USER:
64 %if c.rhodecode_user.username != h.DEFAULT_USER:
63 ${h.form('#', method='get')}
65 ## render template for inline comments
64 <div class="comment-area">
66 ${commentblock.comment_form(form_type='inline')}
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>
116 %else:
67 %else:
117 ${h.form('', class_='inline-form comment-form-login', method='get')}
68 ${h.form('', class_='inline-form comment-form-login', method='get')}
118 <div class="pull-left">
69 <div class="pull-left">
@@ -521,7 +472,6 b' from rhodecode.lib.diffs import NEW_FILE'
521 </%def>
472 </%def>
522
473
523
474
524 <%namespace name="commentblock" file="/changeset/changeset_file_comment.mako"/>
525 <%def name="inline_comments_container(comments)">
475 <%def name="inline_comments_container(comments)">
526 <div class="inline-comments">
476 <div class="inline-comments">
527 %for comment in comments:
477 %for comment in comments:
General Comments 0
You need to be logged in to leave comments. Login now