# HG changeset patch # User Marcin Kuzminski # Date 2020-09-01 10:09:40 # Node ID 3b004b1095e5a51ed86e8aaca207c00896e2c264 # Parent d52ab7abd438a019f39f87691b6d0126b6348425 pull-requests: overhaul of the UX by adding new sidebar - sidebar aggregates comments, todos, observers, referenced tickets, and reviewers - live components push now comments, reviewer presence, and todo live via channelstream - sidebar is collapsable, and stored in user session - simplified number of information on the pull request page - fixed and cleanup of the outdated comments and at version comments that in some cases reported wrong version assignment. diff --git a/rhodecode/apps/repository/__init__.py b/rhodecode/apps/repository/__init__.py --- a/rhodecode/apps/repository/__init__.py +++ b/rhodecode/apps/repository/__init__.py @@ -345,6 +345,16 @@ def includeme(config): pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment/{comment_id}/delete', repo_route=True, repo_accepted_types=['hg', 'git']) + config.add_route( + name='pullrequest_comments', + pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comments', + repo_route=True) + + config.add_route( + name='pullrequest_todos', + pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/todos', + repo_route=True) + # Artifacts, (EE feature) config.add_route( name='repo_artifacts_list', diff --git a/rhodecode/apps/repository/views/repo_commits.py b/rhodecode/apps/repository/views/repo_commits.py --- a/rhodecode/apps/repository/views/repo_commits.py +++ b/rhodecode/apps/repository/views/repo_commits.py @@ -87,6 +87,7 @@ class RepoCommitsView(RepoAppView): diff_limit = c.visual.cut_off_limit_diff file_limit = c.visual.cut_off_limit_file + # get ranges of commit ids if preset commit_range = commit_id_range.split('...')[:2] @@ -226,6 +227,7 @@ class RepoCommitsView(RepoAppView): # sort comments by how they were generated c.comments = sorted(c.comments, key=lambda x: x.comment_id) + c.at_version_num = None if len(c.commit_ranges) == 1: c.commit = c.commit_ranges[0] diff --git a/rhodecode/apps/repository/views/repo_pull_requests.py b/rhodecode/apps/repository/views/repo_pull_requests.py --- a/rhodecode/apps/repository/views/repo_pull_requests.py +++ b/rhodecode/apps/repository/views/repo_pull_requests.py @@ -265,6 +265,36 @@ class RepoPullRequestsView(RepoAppView, return diffset + def register_comments_vars(self, c, pull_request, versions): + comments_model = CommentsModel() + + # GENERAL COMMENTS with versions # + q = comments_model._all_general_comments_of_pull_request(pull_request) + q = q.order_by(ChangesetComment.comment_id.asc()) + general_comments = q + + # pick comments we want to render at current version + c.comment_versions = comments_model.aggregate_comments( + general_comments, versions, c.at_version_num) + + # INLINE COMMENTS with versions # + q = comments_model._all_inline_comments_of_pull_request(pull_request) + q = q.order_by(ChangesetComment.comment_id.asc()) + inline_comments = q + + c.inline_versions = comments_model.aggregate_comments( + inline_comments, versions, c.at_version_num, inline=True) + + # Comments inline+general + if c.at_version: + c.inline_comments_flat = c.inline_versions[c.at_version_num]['display'] + c.comments = c.comment_versions[c.at_version_num]['display'] + else: + c.inline_comments_flat = c.inline_versions[c.at_version_num]['until'] + c.comments = c.comment_versions[c.at_version_num]['until'] + + return general_comments, inline_comments + @LoginRequired() @HasRepoPermissionAnyDecorator( 'repository.read', 'repository.write', 'repository.admin') @@ -280,6 +310,8 @@ class RepoPullRequestsView(RepoAppView, pull_request_id = pull_request.pull_request_id c.state_progressing = pull_request.is_state_changing() + c.pr_broadcast_channel = '/repo${}$/pr/{}'.format( + pull_request.target_repo.repo_name, pull_request.pull_request_id) _new_state = { 'created': PullRequest.STATE_CREATED, @@ -300,22 +332,23 @@ class RepoPullRequestsView(RepoAppView, from_version = self.request.GET.get('from_version') or version merge_checks = self.request.GET.get('merge_checks') c.fulldiff = str2bool(self.request.GET.get('fulldiff')) + force_refresh = str2bool(self.request.GET.get('force_refresh')) + c.range_diff_on = self.request.GET.get('range-diff') == "1" # fetch global flags of ignore ws or context lines diff_context = diffs.get_diff_context(self.request) hide_whitespace_changes = diffs.get_diff_whitespace_flag(self.request) - force_refresh = str2bool(self.request.GET.get('force_refresh')) - (pull_request_latest, pull_request_at_ver, pull_request_display_obj, at_version) = PullRequestModel().get_pr_version( pull_request_id, version=version) + pr_closed = pull_request_latest.is_closed() if pr_closed and (version or from_version): - # not allow to browse versions + # not allow to browse versions for closed PR raise HTTPFound(h.route_path( 'pullrequest_show', repo_name=self.db_repo_name, pull_request_id=pull_request_id)) @@ -323,13 +356,13 @@ class RepoPullRequestsView(RepoAppView, versions = pull_request_display_obj.versions() # used to store per-commit range diffs c.changes = collections.OrderedDict() - c.range_diff_on = self.request.GET.get('range-diff') == "1" c.at_version = at_version c.at_version_num = (at_version - if at_version and at_version != 'latest' + if at_version and at_version != PullRequest.LATEST_VER else None) - c.at_version_pos = ChangesetComment.get_index_from_version( + + c.at_version_index = ChangesetComment.get_index_from_version( c.at_version_num, versions) (prev_pull_request_latest, @@ -340,9 +373,9 @@ class RepoPullRequestsView(RepoAppView, c.from_version = prev_at_version c.from_version_num = (prev_at_version - if prev_at_version and prev_at_version != 'latest' + if prev_at_version and prev_at_version != PullRequest.LATEST_VER else None) - c.from_version_pos = ChangesetComment.get_index_from_version( + c.from_version_index = ChangesetComment.get_index_from_version( c.from_version_num, versions) # define if we're in COMPARE mode or VIEW at version mode @@ -355,14 +388,17 @@ class RepoPullRequestsView(RepoAppView, self.db_repo_name, pull_request_at_ver.target_repo.repo_name) raise HTTPNotFound() - c.shadow_clone_url = PullRequestModel().get_shadow_clone_url( - pull_request_at_ver) + c.shadow_clone_url = PullRequestModel().get_shadow_clone_url(pull_request_at_ver) c.pull_request = pull_request_display_obj c.renderer = pull_request_at_ver.description_renderer or c.renderer c.pull_request_latest = pull_request_latest - if compare or (at_version and not at_version == 'latest'): + # inject latest version + latest_ver = PullRequest.get_pr_display_object(pull_request_latest, pull_request_latest) + c.versions = versions + [latest_ver] + + if compare or (at_version and not at_version == PullRequest.LATEST_VER): c.allowed_to_change_status = False c.allowed_to_update = False c.allowed_to_merge = False @@ -391,12 +427,9 @@ class RepoPullRequestsView(RepoAppView, 'rules' in pull_request_latest.reviewer_data: rules = pull_request_latest.reviewer_data['rules'] or {} try: - c.forbid_adding_reviewers = rules.get( - 'forbid_adding_reviewers') - c.forbid_author_to_review = rules.get( - 'forbid_author_to_review') - c.forbid_commit_author_to_review = rules.get( - 'forbid_commit_author_to_review') + c.forbid_adding_reviewers = rules.get('forbid_adding_reviewers') + c.forbid_author_to_review = rules.get('forbid_author_to_review') + c.forbid_commit_author_to_review = rules.get('forbid_commit_author_to_review') except Exception: pass @@ -421,41 +454,37 @@ class RepoPullRequestsView(RepoAppView, 'rhodecode:templates/pullrequests/pullrequest_merge_checks.mako' return self._get_template_context(c) - comments_model = CommentsModel() + c.allowed_reviewers = [obj.user_id for obj in pull_request.reviewers if obj.user] # reviewers and statuses - c.pull_request_reviewers = pull_request_at_ver.reviewers_statuses() - allowed_reviewers = [x[0].user_id for x in c.pull_request_reviewers] + c.pull_request_default_reviewers_data_json = json.dumps(pull_request.reviewer_data) + c.pull_request_set_reviewers_data_json = collections.OrderedDict({'reviewers': []}) - # GENERAL COMMENTS with versions # - q = comments_model._all_general_comments_of_pull_request(pull_request_latest) - q = q.order_by(ChangesetComment.comment_id.asc()) - general_comments = q + for review_obj, member, reasons, mandatory, status in pull_request_at_ver.reviewers_statuses(): + member_reviewer = h.reviewer_as_json( + member, reasons=reasons, mandatory=mandatory, + user_group=review_obj.rule_user_group_data() + ) - # pick comments we want to render at current version - c.comment_versions = comments_model.aggregate_comments( - general_comments, versions, c.at_version_num) - c.comments = c.comment_versions[c.at_version_num]['until'] + current_review_status = status[0][1].status if status else ChangesetStatus.STATUS_NOT_REVIEWED + member_reviewer['review_status'] = current_review_status + member_reviewer['review_status_label'] = h.commit_status_lbl(current_review_status) + member_reviewer['allowed_to_update'] = c.allowed_to_update + c.pull_request_set_reviewers_data_json['reviewers'].append(member_reviewer) - # INLINE COMMENTS with versions # - q = comments_model._all_inline_comments_of_pull_request(pull_request_latest) - q = q.order_by(ChangesetComment.comment_id.asc()) - inline_comments = q + c.pull_request_set_reviewers_data_json = json.dumps(c.pull_request_set_reviewers_data_json) + + - c.inline_versions = comments_model.aggregate_comments( - inline_comments, versions, c.at_version_num, inline=True) + + general_comments, inline_comments = \ + self.register_comments_vars(c, pull_request_latest, versions) # TODOs c.unresolved_comments = CommentsModel() \ - .get_pull_request_unresolved_todos(pull_request) + .get_pull_request_unresolved_todos(pull_request_latest) c.resolved_comments = CommentsModel() \ - .get_pull_request_resolved_todos(pull_request) - - # inject latest version - latest_ver = PullRequest.get_pr_display_object( - pull_request_latest, pull_request_latest) - - c.versions = versions + [latest_ver] + .get_pull_request_resolved_todos(pull_request_latest) # if we use version, then do not show later comments # than current version @@ -522,8 +551,8 @@ class RepoPullRequestsView(RepoAppView, # empty version means latest, so we keep this to prevent # double caching - version_normalized = version or 'latest' - from_version_normalized = from_version or 'latest' + version_normalized = version or PullRequest.LATEST_VER + from_version_normalized = from_version or PullRequest.LATEST_VER cache_path = self.rhodecode_vcs_repo.get_create_shadow_cache_pr_path(target_repo) cache_file_path = diff_cache_exist( @@ -615,7 +644,7 @@ class RepoPullRequestsView(RepoAppView, diff_limit, file_limit, c.fulldiff, hide_whitespace_changes, diff_context, use_ancestor=use_ancestor - ) + ) # save cached diff if caching_enabled: @@ -719,7 +748,7 @@ class RepoPullRequestsView(RepoAppView, # current user review statuses for each version c.review_versions = {} - if self._rhodecode_user.user_id in allowed_reviewers: + if self._rhodecode_user.user_id in c.allowed_reviewers: for co in general_comments: if co.author.user_id == self._rhodecode_user.user_id: status = co.status_change @@ -939,6 +968,83 @@ class RepoPullRequestsView(RepoAppView, @NotAnonymous() @HasRepoPermissionAnyDecorator( 'repository.read', 'repository.write', 'repository.admin') + @view_config( + route_name='pullrequest_comments', request_method='POST', + renderer='string', xhr=True) + def pullrequest_comments(self): + self.load_default_context() + + pull_request = PullRequest.get_or_404( + self.request.matchdict['pull_request_id']) + pull_request_id = pull_request.pull_request_id + version = self.request.GET.get('version') + + _render = self.request.get_partial_renderer( + 'rhodecode:templates/pullrequests/pullrequest_show.mako') + c = _render.get_call_context() + + (pull_request_latest, + pull_request_at_ver, + pull_request_display_obj, + at_version) = PullRequestModel().get_pr_version( + pull_request_id, version=version) + versions = pull_request_display_obj.versions() + latest_ver = PullRequest.get_pr_display_object(pull_request_latest, pull_request_latest) + c.versions = versions + [latest_ver] + + c.at_version = at_version + c.at_version_num = (at_version + if at_version and at_version != PullRequest.LATEST_VER + else None) + + self.register_comments_vars(c, pull_request_latest, versions) + all_comments = c.inline_comments_flat + c.comments + return _render('comments_table', all_comments, len(all_comments)) + + @LoginRequired() + @NotAnonymous() + @HasRepoPermissionAnyDecorator( + 'repository.read', 'repository.write', 'repository.admin') + @view_config( + route_name='pullrequest_todos', request_method='POST', + renderer='string', xhr=True) + def pullrequest_todos(self): + self.load_default_context() + + pull_request = PullRequest.get_or_404( + self.request.matchdict['pull_request_id']) + pull_request_id = pull_request.pull_request_id + version = self.request.GET.get('version') + + _render = self.request.get_partial_renderer( + 'rhodecode:templates/pullrequests/pullrequest_show.mako') + c = _render.get_call_context() + (pull_request_latest, + pull_request_at_ver, + pull_request_display_obj, + at_version) = PullRequestModel().get_pr_version( + pull_request_id, version=version) + versions = pull_request_display_obj.versions() + latest_ver = PullRequest.get_pr_display_object(pull_request_latest, pull_request_latest) + c.versions = versions + [latest_ver] + + c.at_version = at_version + c.at_version_num = (at_version + if at_version and at_version != PullRequest.LATEST_VER + else None) + + c.unresolved_comments = CommentsModel() \ + .get_pull_request_unresolved_todos(pull_request) + c.resolved_comments = CommentsModel() \ + .get_pull_request_resolved_todos(pull_request) + + all_comments = c.unresolved_comments + c.resolved_comments + return _render('comments_table', all_comments, len(c.unresolved_comments), todo_comments=True) + + @LoginRequired() + @NotAnonymous() + @HasRepoPermissionAnyDecorator( + 'repository.read', 'repository.write', 'repository.admin') @CSRFRequired() @view_config( route_name='pullrequest_create', request_method='POST', @@ -1100,7 +1206,7 @@ class RepoPullRequestsView(RepoAppView, self.request.matchdict['pull_request_id']) _ = self.request.translate - self.load_default_context() + c = self.load_default_context() redirect_url = None if pull_request.is_closed(): @@ -1111,6 +1217,8 @@ class RepoPullRequestsView(RepoAppView, 'redirect_url': redirect_url} is_state_changing = pull_request.is_state_changing() + c.pr_broadcast_channel = '/repo${}$/pr/{}'.format( + pull_request.target_repo.repo_name, pull_request.pull_request_id) # only owner or admin can update it allowed_to_update = PullRequestModel().check_user_update( @@ -1134,7 +1242,7 @@ class RepoPullRequestsView(RepoAppView, return {'response': True, 'redirect_url': redirect_url} - self._update_commits(pull_request) + self._update_commits(c, pull_request) if force_refresh: redirect_url = h.route_path( 'pullrequest_show', repo_name=self.db_repo_name, @@ -1170,7 +1278,7 @@ class RepoPullRequestsView(RepoAppView, h.flash(msg, category='success') return - def _update_commits(self, pull_request): + def _update_commits(self, c, pull_request): _ = self.request.translate with pull_request.set_state(PullRequest.STATE_UPDATING): @@ -1198,13 +1306,18 @@ class RepoPullRequestsView(RepoAppView, change_source=changed) h.flash(msg, category='success') - channel = '/repo${}$/pr/{}'.format( - pull_request.target_repo.repo_name, pull_request.pull_request_id) message = msg + ( ' - ' '{}'.format(_('Reload page'))) + + message_obj = { + 'message': message, + 'level': 'success', + 'topic': '/notifications' + } + channelstream.post_message( - channel, message, self._rhodecode_user.username, + c.pr_broadcast_channel, message_obj, self._rhodecode_user.username, registry=self.request.registry) else: msg = PullRequestModel.UPDATE_STATUS_MESSAGES[resp.reason] @@ -1474,6 +1587,7 @@ class RepoPullRequestsView(RepoAppView, } if comment: c.co = comment + c.at_version_num = None rendered_comment = render( 'rhodecode:templates/changeset/changeset_comment_block.mako', self._get_template_context(c), self.request) diff --git a/rhodecode/lib/helpers.py b/rhodecode/lib/helpers.py --- a/rhodecode/lib/helpers.py +++ b/rhodecode/lib/helpers.py @@ -810,8 +810,7 @@ import tzlocal local_timezone = tzlocal.get_localzone() -def age_component(datetime_iso, value=None, time_is_local=False, tooltip=True): - title = value or format_date(datetime_iso) +def get_timezone(datetime_iso, time_is_local=False): tzinfo = '+00:00' # detect if we have a timezone info, otherwise, add it @@ -822,6 +821,12 @@ def age_component(datetime_iso, value=No timezone = force_timezone or local_timezone offset = timezone.localize(datetime_iso).strftime('%z') tzinfo = '{}:{}'.format(offset[:-2], offset[-2:]) + return tzinfo + + +def age_component(datetime_iso, value=None, time_is_local=False, tooltip=True): + title = value or format_date(datetime_iso) + tzinfo = get_timezone(datetime_iso, time_is_local=time_is_local) return literal( ''.format( @@ -1650,17 +1655,18 @@ def get_active_pattern_entries(repo_name pr_pattern_re = re.compile(r'(?:(?:^!)|(?: !))(\d+)') +allowed_link_formats = [ + 'html', 'rst', 'markdown', 'html+hovercard', 'rst+hovercard', 'markdown+hovercard'] + def process_patterns(text_string, repo_name, link_format='html', active_entries=None): - allowed_formats = ['html', 'rst', 'markdown', - 'html+hovercard', 'rst+hovercard', 'markdown+hovercard'] - if link_format not in allowed_formats: + if link_format not in allowed_link_formats: raise ValueError('Link format can be only one of:{} got {}'.format( - allowed_formats, link_format)) + allowed_link_formats, link_format)) if active_entries is None: - log.debug('Fetch active patterns for repo: %s', repo_name) + log.debug('Fetch active issue tracker patterns for repo: %s', repo_name) active_entries = get_active_pattern_entries(repo_name) issues_data = [] @@ -1718,7 +1724,8 @@ def process_patterns(text_string, repo_n return new_text, issues_data -def urlify_commit_message(commit_text, repository=None, active_pattern_entries=None): +def urlify_commit_message(commit_text, repository=None, active_pattern_entries=None, + issues_container=None): """ Parses given text message and makes proper links. issues are linked to given issue-server, and rest is a commit link @@ -1741,6 +1748,9 @@ def urlify_commit_message(commit_text, r new_text, issues = process_patterns(new_text, repository or '', active_entries=active_pattern_entries) + if issues_container is not None: + issues_container.extend(issues) + return literal(new_text) @@ -1781,7 +1791,7 @@ def renderer_from_filename(filename, exc def render(source, renderer='rst', mentions=False, relative_urls=None, - repo_name=None, active_pattern_entries=None): + repo_name=None, active_pattern_entries=None, issues_container=None): def maybe_convert_relative_links(html_source): if relative_urls: @@ -1798,6 +1808,8 @@ def render(source, renderer='rst', menti source, issues = process_patterns( source, repo_name, link_format='rst', active_entries=active_pattern_entries) + if issues_container is not None: + issues_container.extend(issues) return literal( '
%s
' % @@ -1810,6 +1822,8 @@ def render(source, renderer='rst', menti source, issues = process_patterns( source, repo_name, link_format='markdown', active_entries=active_pattern_entries) + if issues_container is not None: + issues_container.extend(issues) return literal( '
%s
' % diff --git a/rhodecode/model/comment.py b/rhodecode/model/comment.py --- a/rhodecode/model/comment.py +++ b/rhodecode/model/comment.py @@ -91,8 +91,7 @@ class CommentsModel(BaseModel): # group by versions, and count until, and display objects comment_groups = collections.defaultdict(list) - [comment_groups[ - _co.pull_request_version_id].append(_co) for _co in comments] + [comment_groups[_co.pull_request_version_id].append(_co) for _co in comments] def yield_comments(pos): for co in comment_groups[pos]: @@ -456,38 +455,54 @@ class CommentsModel(BaseModel): else: action = 'repo.commit.comment.create' + comment_id = comment.comment_id comment_data = comment.get_api_data() + self._log_audit_action( action, {'data': comment_data}, auth_user, comment) - msg_url = '' channel = None if commit_obj: - msg_url = commit_comment_url repo_name = repo.repo_name channel = u'/repo${}$/commit/{}'.format( repo_name, commit_obj.raw_id ) elif pull_request_obj: - msg_url = pr_comment_url repo_name = pr_target_repo.repo_name channel = u'/repo${}$/pr/{}'.format( repo_name, - pull_request_id + pull_request_obj.pull_request_id ) - message = '{} {} - ' \ - '' \ - '{}' - message = message.format( - user.username, _('made a comment'), msg_url, - _('Show it now')) + if channel: + username = user.username + message = '{} {} #{}, {}' + message = message.format( + username, + _('posted a new comment'), + comment_id, + _('Refresh the page to see new comments.')) - channelstream.post_message( - channel, message, user.username, - registry=get_current_registry()) + message_obj = { + 'message': message, + 'level': 'success', + 'topic': '/notifications' + } + + channelstream.post_message( + channel, message_obj, user.username, + registry=get_current_registry()) + + message_obj = { + 'message': None, + 'user': username, + 'comment_id': comment_id, + 'topic': '/comment' + } + channelstream.post_message( + channel, message_obj, user.username, + registry=get_current_registry()) return comment @@ -642,15 +657,15 @@ class CommentsModel(BaseModel): return self._group_comments_by_path_and_line_number(q) def get_inline_comments_as_list(self, inline_comments, skip_outdated=True, - version=None): - inline_cnt = 0 + version=None): + inline_comms = [] for fname, per_line_comments in inline_comments.iteritems(): for lno, comments in per_line_comments.iteritems(): for comm in comments: if not comm.outdated_at_version(version) and skip_outdated: - inline_cnt += 1 + inline_comms.append(comm) - return inline_cnt + return inline_comms def get_outdated_comments(self, repo_id, pull_request): # TODO: johbo: Remove `repo_id`, it is not needed to find the comments diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -3821,16 +3821,35 @@ class ChangesetComment(Base, BaseModel): """ Checks if comment is outdated for given pull request version """ - return self.outdated and self.pull_request_version_id != version + def version_check(): + return self.pull_request_version_id and self.pull_request_version_id != version + + if self.is_inline: + return self.outdated and version_check() + else: + # general comments don't have .outdated set, also latest don't have a version + return version_check() + + def outdated_at_version_js(self, version): + """ + Checks if comment is outdated for given pull request version + """ + return json.dumps(self.outdated_at_version(version)) def older_than_version(self, version): """ Checks if comment is made from previous version than given """ if version is None: - return self.pull_request_version_id is not None - - return self.pull_request_version_id < version + return self.pull_request_version != version + + return self.pull_request_version < version + + def older_than_version_js(self, version): + """ + Checks if comment is made from previous version than given + """ + return json.dumps(self.older_than_version(version)) @property def commit_id(self): @@ -4327,6 +4346,7 @@ class PullRequest(Base, _PullRequestBase __table_args__ = ( base_table_args, ) + LATEST_VER = 'latest' pull_request_id = Column( 'pull_request_id', Integer(), nullable=False, primary_key=True) @@ -4385,6 +4405,10 @@ class PullRequest(Base, _PullRequestBase def pull_request_version_id(self): return getattr(pull_request_obj, 'pull_request_version_id', None) + @property + def pull_request_last_version(self): + return pull_request_obj.pull_request_last_version + attrs = StrictAttributeDict(pull_request_obj.get_api_data(with_merge_state=False)) attrs.author = StrictAttributeDict( @@ -4449,6 +4473,10 @@ class PullRequest(Base, _PullRequestBase """ return self.versions.count() + 1 + @property + def pull_request_last_version(self): + return self.versions_count + class PullRequestVersion(Base, _PullRequestBase): __tablename__ = 'pull_request_versions' diff --git a/rhodecode/public/css/legacy_code_styles.less b/rhodecode/public/css/legacy_code_styles.less --- a/rhodecode/public/css/legacy_code_styles.less +++ b/rhodecode/public/css/legacy_code_styles.less @@ -240,14 +240,14 @@ div.markdown-block ol { div.markdown-block ul.checkbox li, div.markdown-block ol.checkbox li { list-style: none !important; - margin: 6px !important; + margin: 0px !important; padding: 0 !important; } div.markdown-block ul li, div.markdown-block ol li { list-style: disc !important; - margin: 6px !important; + margin: 0px !important; padding: 0 !important; } diff --git a/rhodecode/public/css/main.less b/rhodecode/public/css/main.less --- a/rhodecode/public/css/main.less +++ b/rhodecode/public/css/main.less @@ -1510,18 +1510,14 @@ table.integrations { min-height: 55px; } -.reviewers_member { - width: 100%; - overflow: auto; -} .reviewer_reason { padding-left: 20px; line-height: 1.5em; } .reviewer_status { display: inline-block; - width: 25px; - min-width: 25px; + width: 20px; + min-width: 20px; height: 1.2em; line-height: 1em; } @@ -1544,23 +1540,17 @@ table.integrations { } .reviewer_member_mandatory { - position: absolute; - left: 15px; - top: 8px; width: 16px; font-size: 11px; margin: 0; padding: 0; color: black; + opacity: 0.4; } .reviewer_member_mandatory_remove, .reviewer_member_remove { - position: absolute; - right: 0; - top: 0; width: 16px; - margin-bottom: 10px; padding: 0; color: black; } @@ -1617,7 +1607,8 @@ table.integrations { .td-todo-number { text-align: left; white-space: nowrap; - width: 15%; + width: 1%; + padding-right: 2px; } .td-todo-gravatar { @@ -1641,10 +1632,13 @@ table.integrations { text-overflow: ellipsis; } +table.group_members { + width: 100% +} + .group_members { margin-top: 0; padding: 0; - list-style: outside none none; img { height: @gravatar-size; 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 @@ -246,6 +246,8 @@ function registerRCRoutes() { pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']); pyroutes.register('pullrequest_comment_edit', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/edit', ['repo_name', 'pull_request_id', 'comment_id']); pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']); + pyroutes.register('pullrequest_comments', '/%(repo_name)s/pull-request/%(pull_request_id)s/comments', ['repo_name', 'pull_request_id']); + pyroutes.register('pullrequest_todos', '/%(repo_name)s/pull-request/%(pull_request_id)s/todos', ['repo_name', 'pull_request_id']); pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']); pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']); pyroutes.register('edit_repo_advanced_archive', '/%(repo_name)s/settings/advanced/archive', ['repo_name']); diff --git a/rhodecode/public/js/src/components/rhodecode-app/rhodecode-app.js b/rhodecode/public/js/src/components/rhodecode-app/rhodecode-app.js --- a/rhodecode/public/js/src/components/rhodecode-app/rhodecode-app.js +++ b/rhodecode/public/js/src/components/rhodecode-app/rhodecode-app.js @@ -28,9 +28,12 @@ export class RhodecodeApp extends Polyme super.connectedCallback(); ccLog.debug('rhodeCodeApp created'); $.Topic('/notifications').subscribe(this.handleNotifications.bind(this)); + $.Topic('/comment').subscribe(this.handleComment.bind(this)); $.Topic('/favicon/update').subscribe(this.faviconUpdate.bind(this)); $.Topic('/connection_controller/subscribe').subscribe( - this.subscribeToChannelTopic.bind(this)); + this.subscribeToChannelTopic.bind(this) + ); + // this event can be used to coordinate plugins to do their // initialization before channelstream is kicked off $.Topic('/__MAIN_APP__').publish({}); @@ -71,6 +74,14 @@ export class RhodecodeApp extends Polyme } + handleComment(data) { + if (data.message.comment_id) { + if (window.refreshAllComments !== undefined) { + refreshAllComments() + } + } + } + faviconUpdate(data) { this.shadowRoot.querySelector('rhodecode-favicon').counter = data.count; } @@ -95,6 +106,7 @@ export class RhodecodeApp extends Polyme } // append any additional channels registered in other plugins $.Topic('/connection_controller/subscribe').processPrepared(); + channelstreamConnection.connect(); } } @@ -157,8 +169,7 @@ export class RhodecodeApp extends Polyme handleConnected(event) { var channelstreamConnection = this.getChannelStreamConnection(); - channelstreamConnection.set('channelsState', - event.detail.channels_info); + channelstreamConnection.set('channelsState', event.detail.channels_info); channelstreamConnection.set('userState', event.detail.state); channelstreamConnection.set('channels', event.detail.channels); this.propagageChannelsState(); diff --git a/rhodecode/public/js/src/rhodecode.js b/rhodecode/public/js/src/rhodecode.js --- a/rhodecode/public/js/src/rhodecode.js +++ b/rhodecode/public/js/src/rhodecode.js @@ -677,7 +677,9 @@ var feedLifetimeOptions = function(query query.callback(data); }; - +/* +* Retrievew via templateContext.session_attrs.key +* */ var storeUserSessionAttr = function (key, val) { var postData = { 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 @@ -558,7 +558,7 @@ var CommentsController = function() { return false; }; - this.showVersion = function (comment_id, comment_history_id) { + this.showVersion = function (comment_id, comment_history_id) { var historyViewUrl = pyroutes.url( 'repo_commit_comment_history_view', @@ -585,7 +585,7 @@ var CommentsController = function() { successRenderCommit, failRenderCommit ); - }; + }; this.getLineNumber = function(node) { var $node = $(node); @@ -670,8 +670,20 @@ var CommentsController = function() { var success = function(response) { $comment.remove(); + + if (window.updateSticky !== undefined) { + // potentially our comments change the active window size, so we + // notify sticky elements + updateSticky() + } + + if (window.refreshAllComments !== undefined) { + // if we have this handler, run it, and refresh all comments boxes + refreshAllComments() + } return false; }; + var failure = function(jqXHR, textStatus, errorThrown) { var prefix = "Error while deleting this comment.\n" var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix); @@ -682,6 +694,9 @@ var CommentsController = function() { return false; }; ajaxPOST(url, postData, success, failure); + + + } this.deleteComment = function(node) { @@ -727,6 +742,15 @@ var CommentsController = function() { $filediff.find('.hide-line-comments').removeClass('hide-line-comments'); $filediff.toggleClass('hide-comments'); } + + // since we change the height of the diff container that has anchor points for upper + // sticky header, we need to tell it to re-calculate those + if (window.updateSticky !== undefined) { + // potentially our comments change the active window size, so we + // notify sticky elements + updateSticky() + } + return false; }; @@ -747,7 +771,7 @@ var CommentsController = function() { var cm = commentForm.getCmInstance(); if (resolvesCommentId){ - var placeholderText = _gettext('Leave a resolution comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId); + placeholderText = _gettext('Leave a resolution comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId); } setTimeout(function() { @@ -1077,9 +1101,15 @@ var CommentsController = function() { updateSticky() } + if (window.refreshAllComments !== undefined) { + // if we have this handler, run it, and refresh all comments boxes + refreshAllComments() + } + commentForm.setActionButtonsDisabled(false); }; + var submitFailCallback = function(jqXHR, textStatus, errorThrown) { var prefix = "Error while editing comment.\n" var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix); @@ -1209,6 +1239,11 @@ var CommentsController = function() { updateSticky() } + if (window.refreshAllComments !== undefined) { + // if we have this handler, run it, and refresh all comments boxes + refreshAllComments() + } + commentForm.setActionButtonsDisabled(false); }; diff --git a/rhodecode/public/js/src/rhodecode/pullrequests.js b/rhodecode/public/js/src/rhodecode/pullrequests.js --- a/rhodecode/public/js/src/rhodecode/pullrequests.js +++ b/rhodecode/public/js/src/rhodecode/pullrequests.js @@ -98,10 +98,13 @@ ReviewersController = function () { var self = this; this.$reviewRulesContainer = $('#review_rules'); this.$rulesList = this.$reviewRulesContainer.find('.pr-reviewer-rules'); + this.$userRule = $('.pr-user-rule-container'); this.forbidReviewUsers = undefined; this.$reviewMembers = $('#review_members'); this.currentRequest = null; this.diffData = null; + this.enabledRules = []; + //dummy handler, we might register our own later this.diffDataHandler = function(data){}; @@ -116,14 +119,17 @@ ReviewersController = function () { this.hideReviewRules = function () { self.$reviewRulesContainer.hide(); + $(self.$userRule.selector).hide(); }; this.showReviewRules = function () { self.$reviewRulesContainer.show(); + $(self.$userRule.selector).show(); }; this.addRule = function (ruleText) { self.showReviewRules(); + self.enabledRules.push(ruleText); return '
- {0}
'.format(ruleText) }; @@ -179,6 +185,7 @@ ReviewersController = function () { _gettext('Reviewers picked from source code changes.')) ) } + if (data.rules.forbid_adding_reviewers) { $('#add_reviewer_input').remove(); self.$rulesList.append( @@ -186,6 +193,7 @@ ReviewersController = function () { _gettext('Adding new reviewers is forbidden.')) ) } + if (data.rules.forbid_author_to_review) { self.forbidReviewUsers.push(data.rules_data.pr_author); self.$rulesList.append( @@ -193,6 +201,7 @@ ReviewersController = function () { _gettext('Author is not allowed to be a reviewer.')) ) } + if (data.rules.forbid_commit_author_to_review) { if (data.rules_data.forbidden_users) { @@ -208,6 +217,12 @@ ReviewersController = function () { ) } + // we don't have any rules set, so we inform users about it + if (self.enabledRules.length === 0) { + self.addRule( + _gettext('No review rules set.')) + } + return self.forbidReviewUsers }; @@ -347,11 +362,12 @@ ReviewersController = function () { members.innerHTML += renderTemplate('reviewMemberEntry', { 'member': reviewer_obj, 'mandatory': mandatory, + 'reasons': reasons, 'allowed_to_update': true, 'review_status': 'not_reviewed', 'review_status_label': _gettext('Not Reviewed'), - 'reasons': reasons, - 'create': true + 'user_group': reviewer_obj.user_group, + 'create': true, }); tooltipActivate(); } @@ -600,7 +616,7 @@ VersionController = function () { var $elem = $(elem); var $target = $(target); - if ($target.is(':visible')) { + if ($target.is(':visible') || $target.length === 0) { $target.hide(); $elem.html($elem.data('toggleOn')) } else { diff --git a/rhodecode/templates/changeset/changeset_file_comment.mako b/rhodecode/templates/changeset/changeset_file_comment.mako --- a/rhodecode/templates/changeset/changeset_file_comment.mako +++ b/rhodecode/templates/changeset/changeset_file_comment.mako @@ -10,12 +10,14 @@ <%namespace name="base" file="/base/base.mako"/> <%def name="comment_block(comment, inline=False, active_pattern_entries=None)"> - <% pr_index_ver = comment.get_index_version(getattr(c, 'versions', [])) %> + + <% comment_ver = comment.get_index_version(getattr(c, 'versions', [])) %> <% latest_ver = len(getattr(c, 'versions', [])) %> + % if inline: - <% outdated_at_ver = comment.outdated_at_version(getattr(c, 'at_version_num', None)) %> + <% outdated_at_ver = comment.outdated_at_version(c.at_version_num) %> % else: - <% outdated_at_ver = comment.older_than_version(getattr(c, 'at_version_num', None)) %> + <% outdated_at_ver = comment.older_than_version(c.at_version_num) %> % endif
¶ +