diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py --- a/rhodecode/config/routing.py +++ b/rhodecode/config/routing.py @@ -694,8 +694,8 @@ def make_map(config): requirements=URL_NAME_REQUIREMENTS, jsroute=True) rmap.connect('repo_refs_data', '/{repo_name}/refs-data', - controller='summary', action='repo_refs_data', jsroute=True, - requirements=URL_NAME_REQUIREMENTS) + controller='summary', action='repo_refs_data', + requirements=URL_NAME_REQUIREMENTS, jsroute=True) rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog', controller='summary', action='repo_refs_changelog_data', requirements=URL_NAME_REQUIREMENTS, jsroute=True) @@ -704,9 +704,9 @@ def make_map(config): jsroute=True, requirements=URL_NAME_REQUIREMENTS) rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}', - controller='changeset', revision='tip', jsroute=True, + controller='changeset', revision='tip', conditions={'function': check_repo}, - requirements=URL_NAME_REQUIREMENTS) + requirements=URL_NAME_REQUIREMENTS, jsroute=True) rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}', controller='changeset', revision='tip', action='changeset_children', conditions={'function': check_repo}, @@ -923,7 +923,7 @@ def make_map(config): controller='pullrequests', action='show', conditions={'function': check_repo, 'method': ['GET']}, - requirements=URL_NAME_REQUIREMENTS) + requirements=URL_NAME_REQUIREMENTS, jsroute=True) rmap.connect('pullrequest_update', '/{repo_name}/pull-request/{pull_request_id}', diff --git a/rhodecode/controllers/pullrequests.py b/rhodecode/controllers/pullrequests.py --- a/rhodecode/controllers/pullrequests.py +++ b/rhodecode/controllers/pullrequests.py @@ -904,6 +904,8 @@ class PullrequestsController(BaseRepoCon status = request.POST.get('changeset_status', None) text = request.POST.get('text') comment_type = request.POST.get('comment_type') + resolves_comment_id = request.POST.get('resolves_comment_id') + if status and '_closed' in status: close_pr = True status = status.replace('_closed', '') @@ -936,7 +938,8 @@ class PullrequestsController(BaseRepoCon status_change_type=(status if status and allowed_to_change_status else None), closing_pr=close_pr, - comment_type=comment_type + comment_type=comment_type, + resolves_comment_id=resolves_comment_id ) if allowed_to_change_status: diff --git a/rhodecode/model/comment.py b/rhodecode/model/comment.py --- a/rhodecode/model/comment.py +++ b/rhodecode/model/comment.py @@ -84,9 +84,10 @@ class CommentsModel(BaseModel): return global_renderer def create(self, text, repo, user, commit_id=None, pull_request=None, - f_path=None, line_no=None, status_change=None, comment_type=None, - status_change_type=None, closing_pr=False, - send_email=True, renderer=None): + f_path=None, line_no=None, status_change=None, + status_change_type=None, comment_type=None, + resolves_comment_id=None, closing_pr=False, send_email=True, + renderer=None): """ Creates new comment for commit or pull request. IF status_change is not none this comment is associated with a @@ -113,6 +114,8 @@ class CommentsModel(BaseModel): if not renderer: renderer = self._get_renderer() + repo = self._get_repo(repo) + user = self._get_user(user) schema = comment_schema.CommentSchema() validated_kwargs = schema.deserialize(dict( @@ -121,15 +124,12 @@ class CommentsModel(BaseModel): comment_file=f_path, comment_line=line_no, renderer_type=renderer, - status_change=status_change, - - repo=repo, - user=user, + status_change=status_change_type, + resolves_comment_id=resolves_comment_id, + repo=repo.repo_id, + user=user.user_id, )) - repo = self._get_repo(validated_kwargs['repo']) - user = self._get_user(validated_kwargs['user']) - comment = ChangesetComment() comment.renderer = validated_kwargs['renderer_type'] comment.text = validated_kwargs['comment_body'] @@ -139,6 +139,8 @@ class CommentsModel(BaseModel): comment.repo = repo comment.author = user + comment.resolved_comment = self.__get_commit_comment( + validated_kwargs['resolves_comment_id']) pull_request_id = pull_request diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -2917,7 +2917,7 @@ class ChangesetComment(Base, BaseModel): comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE) resolved_comment_id = Column('resolved_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=True) - resolved_comment = relationship('ChangesetComment', remote_side=comment_id) + resolved_comment = relationship('ChangesetComment', remote_side=comment_id, backref='resolved_by') author = relationship('User', lazy='joined') repo = relationship('Repository') status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan") @@ -2959,6 +2959,10 @@ class ChangesetComment(Base, BaseModel): """ return self.outdated and self.pull_request_version_id != version + @property + def resolved(self): + return self.resolved_by[0] if self.resolved_by else None + def get_index_version(self, versions): return self.get_index_from_version( self.pull_request_version_id, versions) diff --git a/rhodecode/model/validation_schema/schemas/comment_schema.py b/rhodecode/model/validation_schema/schemas/comment_schema.py --- a/rhodecode/model/validation_schema/schemas/comment_schema.py +++ b/rhodecode/model/validation_schema/schemas/comment_schema.py @@ -53,18 +53,22 @@ comment_types = ['note', 'todo'] class CommentSchema(colander.MappingSchema): - from rhodecode.model.db import ChangesetComment + from rhodecode.model.db import ChangesetComment, ChangesetStatus comment_body = colander.SchemaNode(colander.String()) comment_type = colander.SchemaNode( colander.String(), - validator=colander.OneOf(ChangesetComment.COMMENT_TYPES)) + validator=colander.OneOf(ChangesetComment.COMMENT_TYPES), + missing=ChangesetComment.COMMENT_TYPE_NOTE) comment_file = colander.SchemaNode(colander.String(), missing=None) comment_line = colander.SchemaNode(colander.String(), missing=None) - status_change = colander.SchemaNode(colander.String(), missing=None) + status_change = colander.SchemaNode( + colander.String(), missing=None, + validator=colander.OneOf([x[0] for x in ChangesetStatus.STATUSES])) renderer_type = colander.SchemaNode(colander.String()) - # do those ? + resolves_comment_id = colander.SchemaNode(colander.Integer(), missing=None) + user = colander.SchemaNode(types.StrOrIntType()) repo = colander.SchemaNode(types.StrOrIntType()) diff --git a/rhodecode/public/css/comments.less b/rhodecode/public/css/comments.less --- a/rhodecode/public/css/comments.less +++ b/rhodecode/public/css/comments.less @@ -66,6 +66,7 @@ tr.inline-comments div { white-space: nowrap; text-transform: uppercase; + min-width: 40px; &.todo { color: @color5; @@ -366,7 +367,7 @@ form.comment-form { display: inline-block; } - .comment-button .comment-button-input { + .comment-button-input { margin-right: 0; } diff --git a/rhodecode/public/js/rhodecode/routes.js b/rhodecode/public/js/rhodecode/routes.js --- a/rhodecode/public/js/rhodecode/routes.js +++ b/rhodecode/public/js/rhodecode/routes.js @@ -37,6 +37,7 @@ function registerRCRoutes() { pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']); pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']); pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']); + pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']); pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']); pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']); pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']); diff --git a/rhodecode/public/js/src/rhodecode/comments.js b/rhodecode/public/js/src/rhodecode/comments.js --- a/rhodecode/public/js/src/rhodecode/comments.js +++ b/rhodecode/public/js/src/rhodecode/comments.js @@ -79,7 +79,7 @@ var bindToggleButtons = function() { }; var linkifyComments = function(comments) { - /* TODO: dan: remove this - it should no longer needed */ + /* TODO: marcink: remove this - it should no longer needed */ for (var i = 0; i < comments.length; i++) { var comment_id = $(comments[i]).data('comment-id'); var prev_comment_id = $(comments[i - 1]).data('comment-id'); @@ -96,6 +96,8 @@ var linkifyComments = function(comments) $('#next_c_' + comment_id + " a.arrow_comment_link").attr( 'href', '#comment-' + next_comment_id).removeClass('disabled'); } + /* TODO(marcink): end removal here */ + // place a first link to the total counter if (i === 0) { $('#inline-comments-counter').attr('href', '#comment-' + comment_id); @@ -106,10 +108,23 @@ var linkifyComments = function(comments) /* Comment form for main and inline comments */ -var CommentForm = (function() { + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + module.exports = mod(); + else // Plain browser env + (this || window).CommentForm = mod(); + +})(function() { "use strict"; - function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions) { + function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId) { + if (!(this instanceof CommentForm)) { + return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId); + } + + // bind the element instance to our Form + $(formElement).get(0).CommentForm = this; this.withLineNo = function(selector) { var lineNo = this.lineNo; @@ -135,6 +150,9 @@ var CommentForm = (function() { this.cancelButton = this.withLineNo('#cancel-btn'); this.commentType = this.withLineNo('#comment_type'); + this.resolvesId = null; + this.resolvesActionId = null; + this.cmBox = this.withLineNo('#text'); this.cm = initCommentBoxCodeMirror(this.cmBox, this.initAutocompleteActions); @@ -147,17 +165,38 @@ var CommentForm = (function() { this.previewUrl = pyroutes.url('changeset_comment_preview', {'repo_name': templateContext.repo_name}); - // based on commitId, or pullReuqestId decide where do we submit + if (resolvesCommentId){ + this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId); + this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId); + $(this.commentType).prop('disabled', true); + $(this.commentType).addClass('disabled'); + + var resolvedInfo = ( + '
--
- ${_('Write')}
-
- -
- ${_('Preview')}
-
- -
-
-
-
---
- ${_('Write')}
-
- -
- ${_('Preview')}
-
- -
-
-
-
-