##// END OF EJS Templates
commits/pr pages various fixes....
marcink -
r4485:ac1b264f default
parent child Browse files
Show More
@@ -0,0 +1,134 b''
1 ## snippet for sidebar elements
2 ## usage:
3 ## <%namespace name="sidebar" file="/base/sidebar.mako"/>
4 ## ${sidebar.comments_table()}
5 <%namespace name="base" file="/base/base.mako"/>
6
7 <%def name="comments_table(comments, counter_num, todo_comments=False, existing_ids=None, is_pr=True)">
8 <%
9 if todo_comments:
10 cls_ = 'todos-content-table'
11 def sorter(entry):
12 user_id = entry.author.user_id
13 resolved = '1' if entry.resolved else '0'
14 if user_id == c.rhodecode_user.user_id:
15 # own comments first
16 user_id = 0
17 return '{}'.format(str(entry.comment_id).zfill(10000))
18 else:
19 cls_ = 'comments-content-table'
20 def sorter(entry):
21 user_id = entry.author.user_id
22 return '{}'.format(str(entry.comment_id).zfill(10000))
23
24 existing_ids = existing_ids or []
25
26 %>
27
28 <table class="todo-table ${cls_}" data-total-count="${len(comments)}" data-counter="${counter_num}">
29
30 % for loop_obj, comment_obj in h.looper(reversed(sorted(comments, key=sorter))):
31 <%
32 display = ''
33 _cls = ''
34 %>
35
36 <%
37 comment_ver_index = comment_obj.get_index_version(getattr(c, 'versions', []))
38 prev_comment_ver_index = 0
39 if loop_obj.previous:
40 prev_comment_ver_index = loop_obj.previous.get_index_version(getattr(c, 'versions', []))
41
42 ver_info = None
43 if getattr(c, 'versions', []):
44 ver_info = c.versions[comment_ver_index-1] if comment_ver_index else None
45 %>
46 <% hidden_at_ver = comment_obj.outdated_at_version_js(c.at_version_num) %>
47 <% is_from_old_ver = comment_obj.older_than_version_js(c.at_version_num) %>
48 <%
49 if (prev_comment_ver_index > comment_ver_index):
50 comments_ver_divider = comment_ver_index
51 else:
52 comments_ver_divider = None
53 %>
54
55 % if todo_comments:
56 % if comment_obj.resolved:
57 <% _cls = 'resolved-todo' %>
58 <% display = 'none' %>
59 % endif
60 % else:
61 ## SKIP TODOs we display them in other area
62 % if comment_obj.is_todo:
63 <% display = 'none' %>
64 % endif
65 ## Skip outdated comments
66 % if comment_obj.outdated:
67 <% display = 'none' %>
68 <% _cls = 'hidden-comment' %>
69 % endif
70 % endif
71
72 % if not todo_comments and comments_ver_divider:
73 <tr class="old-comments-marker">
74 <td colspan="3">
75 % if ver_info:
76 <code>v${comments_ver_divider} ${h.age_component(ver_info.created_on, time_is_local=True, tooltip=False)}</code>
77 % else:
78 <code>v${comments_ver_divider}</code>
79 % endif
80 </td>
81 </tr>
82
83 % endif
84
85 <tr class="${_cls}" style="display: ${display};" data-sidebar-comment-id="${comment_obj.comment_id}">
86 <td class="td-todo-number">
87
88 <a class="${('todo-resolved' if comment_obj.resolved else '')} permalink"
89 href="#comment-${comment_obj.comment_id}"
90 onclick="return Rhodecode.comments.scrollToComment($('#comment-${comment_obj.comment_id}'), 0, ${hidden_at_ver})">
91
92 <%
93 version_info = ''
94 if is_pr:
95 version_info = (' made in older version (v{})'.format(comment_ver_index) if is_from_old_ver == 'true' else ' made in this version')
96 %>
97
98 % if todo_comments:
99 % if comment_obj.is_inline:
100 <i class="tooltip icon-code" title="Inline TODO comment${version_info}."></i>
101 % else:
102 <i class="tooltip icon-comment" title="General TODO comment${version_info}."></i>
103 % endif
104 % else:
105 % if comment_obj.outdated:
106 <i class="tooltip icon-comment-toggle" title="Inline Outdated made in v${comment_ver_index}."></i>
107 % elif comment_obj.is_inline:
108 <i class="tooltip icon-code" title="Inline comment${version_info}."></i>
109 % else:
110 <i class="tooltip icon-comment" title="General comment${version_info}."></i>
111 % endif
112 % endif
113
114 </a>
115 ## NEW, since refresh
116 % if existing_ids and comment_obj.comment_id not in existing_ids:
117 <span class="tag">NEW</span>
118 % endif
119 </td>
120
121 <td class="td-todo-gravatar">
122 ${base.gravatar(comment_obj.author.email, 16, user=comment_obj.author, tooltip=True, extra_class=['no-margin'])}
123 </td>
124 <td class="todo-comment-text-wrapper">
125 <div class="tooltip todo-comment-text timeago ${('todo-resolved' if comment_obj.resolved else '')} " title="${h.format_date(comment_obj.created_on)}" datetime="${comment_obj.created_on}${h.get_timezone(comment_obj.created_on, time_is_local=True)}">
126 <code>${h.chop_at_smart(comment_obj.text, '\n', suffix_if_chopped='...')}</code>
127 </div>
128 </td>
129 </tr>
130 % endfor
131
132 </table>
133
134 </%def> No newline at end of file
@@ -485,23 +485,10 b' class TestRepoCommitCommentsView(TestCon'
485 485
486 486
487 487 def assert_comment_links(response, comments, inline_comments):
488 if comments == 1:
489 comments_text = "%d General" % comments
490 else:
491 comments_text = "%d General" % comments
492
493 if inline_comments == 1:
494 inline_comments_text = "%d Inline" % inline_comments
495 else:
496 inline_comments_text = "%d Inline" % inline_comments
488 response.mustcontain(
489 '<span class="display-none" id="general-comments-count">{}</span>'.format(comments))
490 response.mustcontain(
491 '<span class="display-none" id="inline-comments-count">{}</span>'.format(inline_comments))
497 492
498 if comments:
499 response.mustcontain('<a href="#comments">%s</a>,' % comments_text)
500 else:
501 response.mustcontain(comments_text)
502 493
503 if inline_comments:
504 response.mustcontain(
505 'id="inline-comments-counter">%s' % inline_comments_text)
506 else:
507 response.mustcontain(inline_comments_text)
494
@@ -18,8 +18,8 b''
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21
22 21 import logging
22 import collections
23 23
24 24 from pyramid.httpexceptions import (
25 25 HTTPNotFound, HTTPBadRequest, HTTPFound, HTTPForbidden, HTTPConflict)
@@ -34,14 +34,14 b' from rhodecode.apps.file_store.exception'
34 34 from rhodecode.lib import diffs, codeblocks
35 35 from rhodecode.lib.auth import (
36 36 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous, CSRFRequired)
37
37 from rhodecode.lib.ext_json import json
38 38 from rhodecode.lib.compat import OrderedDict
39 39 from rhodecode.lib.diffs import (
40 40 cache_diff, load_cached_diff, diff_cache_exist, get_diff_context,
41 41 get_diff_whitespace_flag)
42 42 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError, CommentVersionMismatch
43 43 import rhodecode.lib.helpers as h
44 from rhodecode.lib.utils2 import safe_unicode, str2bool
44 from rhodecode.lib.utils2 import safe_unicode, str2bool, StrictAttributeDict
45 45 from rhodecode.lib.vcs.backends.base import EmptyCommit
46 46 from rhodecode.lib.vcs.exceptions import (
47 47 RepositoryError, CommitDoesNotExistError)
@@ -87,7 +87,6 b' class RepoCommitsView(RepoAppView):'
87 87 diff_limit = c.visual.cut_off_limit_diff
88 88 file_limit = c.visual.cut_off_limit_file
89 89
90
91 90 # get ranges of commit ids if preset
92 91 commit_range = commit_id_range.split('...')[:2]
93 92
@@ -116,6 +115,7 b' class RepoCommitsView(RepoAppView):'
116 115 except Exception:
117 116 log.exception("General failure")
118 117 raise HTTPNotFound()
118 single_commit = len(c.commit_ranges) == 1
119 119
120 120 c.changes = OrderedDict()
121 121 c.lines_added = 0
@@ -129,23 +129,48 b' class RepoCommitsView(RepoAppView):'
129 129 c.inline_comments = []
130 130 c.files = []
131 131
132 c.statuses = []
133 132 c.comments = []
134 133 c.unresolved_comments = []
135 134 c.resolved_comments = []
136 if len(c.commit_ranges) == 1:
135
136 # Single commit
137 if single_commit:
137 138 commit = c.commit_ranges[0]
138 139 c.comments = CommentsModel().get_comments(
139 140 self.db_repo.repo_id,
140 141 revision=commit.raw_id)
141 c.statuses.append(ChangesetStatusModel().get_status(
142 self.db_repo.repo_id, commit.raw_id))
142
143 143 # comments from PR
144 144 statuses = ChangesetStatusModel().get_statuses(
145 145 self.db_repo.repo_id, commit.raw_id,
146 146 with_revisions=True)
147 prs = set(st.pull_request for st in statuses
148 if st.pull_request is not None)
147
148 prs = set()
149 reviewers = list()
150 reviewers_duplicates = set() # to not have duplicates from multiple votes
151 for c_status in statuses:
152
153 # extract associated pull-requests from votes
154 if c_status.pull_request:
155 prs.add(c_status.pull_request)
156
157 # extract reviewers
158 _user_id = c_status.author.user_id
159 if _user_id not in reviewers_duplicates:
160 reviewers.append(
161 StrictAttributeDict({
162 'user': c_status.author,
163
164 # fake attributed for commit, page that we don't have
165 # but we share the display with PR page
166 'mandatory': False,
167 'reasons': [],
168 'rule_user_group_data': lambda: None
169 })
170 )
171 reviewers_duplicates.add(_user_id)
172
173 c.allowed_reviewers = reviewers
149 174 # from associated statuses, check the pull requests, and
150 175 # show comments from them
151 176 for pr in prs:
@@ -156,6 +181,37 b' class RepoCommitsView(RepoAppView):'
156 181 c.resolved_comments = CommentsModel()\
157 182 .get_commit_resolved_todos(commit.raw_id)
158 183
184 c.inline_comments_flat = CommentsModel()\
185 .get_commit_inline_comments(commit.raw_id)
186
187 review_statuses = ChangesetStatusModel().aggregate_votes_by_user(
188 statuses, reviewers)
189
190 c.commit_review_status = ChangesetStatus.STATUS_NOT_REVIEWED
191
192 c.commit_set_reviewers_data_json = collections.OrderedDict({'reviewers': []})
193
194 for review_obj, member, reasons, mandatory, status in review_statuses:
195 member_reviewer = h.reviewer_as_json(
196 member, reasons=reasons, mandatory=mandatory,
197 user_group=None
198 )
199
200 current_review_status = status[0][1].status if status else ChangesetStatus.STATUS_NOT_REVIEWED
201 member_reviewer['review_status'] = current_review_status
202 member_reviewer['review_status_label'] = h.commit_status_lbl(current_review_status)
203 member_reviewer['allowed_to_update'] = False
204 c.commit_set_reviewers_data_json['reviewers'].append(member_reviewer)
205
206 c.commit_set_reviewers_data_json = json.dumps(c.commit_set_reviewers_data_json)
207
208 # NOTE(marcink): this uses the same voting logic as in pull-requests
209 c.commit_review_status = ChangesetStatusModel().calculate_status(review_statuses)
210 c.commit_broadcast_channel = u'/repo${}$/commit/{}'.format(
211 c.repo_name,
212 commit.raw_id
213 )
214
159 215 diff = None
160 216 # Iterate over ranges (default commit view is always one commit)
161 217 for commit in c.commit_ranges:
@@ -397,6 +453,7 b' class RepoCommitsView(RepoAppView):'
397 453 }
398 454 if comment:
399 455 c.co = comment
456 c.at_version_num = 0
400 457 rendered_comment = render(
401 458 'rhodecode:templates/changeset/changeset_comment_block.mako',
402 459 self._get_template_context(c), self.request)
@@ -39,7 +39,7 b' from rhodecode.lib.ext_json import json'
39 39 from rhodecode.lib.auth import (
40 40 LoginRequired, HasRepoPermissionAny, HasRepoPermissionAnyDecorator,
41 41 NotAnonymous, CSRFRequired)
42 from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode
42 from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode, safe_int
43 43 from rhodecode.lib.vcs.backends.base import EmptyCommit, UpdateFailureReason
44 44 from rhodecode.lib.vcs.exceptions import (
45 45 CommitDoesNotExistError, RepositoryRequirementError, EmptyRepositoryError)
@@ -474,9 +474,6 b' class RepoPullRequestsView(RepoAppView, '
474 474
475 475 c.pull_request_set_reviewers_data_json = json.dumps(c.pull_request_set_reviewers_data_json)
476 476
477
478
479
480 477 general_comments, inline_comments = \
481 478 self.register_comments_vars(c, pull_request_latest, versions)
482 479
@@ -980,7 +977,7 b' class RepoPullRequestsView(RepoAppView, '
980 977 version = self.request.GET.get('version')
981 978
982 979 _render = self.request.get_partial_renderer(
983 'rhodecode:templates/pullrequests/pullrequest_show.mako')
980 'rhodecode:templates/base/sidebar.mako')
984 981 c = _render.get_call_context()
985 982
986 983 (pull_request_latest,
@@ -999,7 +996,11 b' class RepoPullRequestsView(RepoAppView, '
999 996
1000 997 self.register_comments_vars(c, pull_request_latest, versions)
1001 998 all_comments = c.inline_comments_flat + c.comments
1002 return _render('comments_table', all_comments, len(all_comments))
999
1000 existing_ids = filter(
1001 lambda e: e, map(safe_int, self.request.POST.getall('comments[]')))
1002 return _render('comments_table', all_comments, len(all_comments),
1003 existing_ids=existing_ids)
1003 1004
1004 1005 @LoginRequired()
1005 1006 @NotAnonymous()
@@ -1017,7 +1018,7 b' class RepoPullRequestsView(RepoAppView, '
1017 1018 version = self.request.GET.get('version')
1018 1019
1019 1020 _render = self.request.get_partial_renderer(
1020 'rhodecode:templates/pullrequests/pullrequest_show.mako')
1021 'rhodecode:templates/base/sidebar.mako')
1021 1022 c = _render.get_call_context()
1022 1023 (pull_request_latest,
1023 1024 pull_request_at_ver,
@@ -1039,7 +1040,10 b' class RepoPullRequestsView(RepoAppView, '
1039 1040 .get_pull_request_resolved_todos(pull_request)
1040 1041
1041 1042 all_comments = c.unresolved_comments + c.resolved_comments
1042 return _render('comments_table', all_comments, len(c.unresolved_comments), todo_comments=True)
1043 existing_ids = filter(
1044 lambda e: e, map(safe_int, self.request.POST.getall('comments[]')))
1045 return _render('comments_table', all_comments, len(c.unresolved_comments),
1046 todo_comments=True, existing_ids=existing_ids)
1043 1047
1044 1048 @LoginRequired()
1045 1049 @NotAnonymous()
@@ -354,34 +354,37 b' class ChangesetStatusModel(BaseModel):'
354 354 Session().add(new_status)
355 355 return new_statuses
356 356
357 def aggregate_votes_by_user(self, commit_statuses, reviewers_data):
358
359 commit_statuses_map = collections.defaultdict(list)
360 for st in commit_statuses:
361 commit_statuses_map[st.author.username] += [st]
362
363 reviewers = []
364
365 def version(commit_status):
366 return commit_status.version
367
368 for obj in reviewers_data:
369 if not obj.user:
370 continue
371 statuses = commit_statuses_map.get(obj.user.username, None)
372 if statuses:
373 status_groups = itertools.groupby(
374 sorted(statuses, key=version), version)
375 statuses = [(x, list(y)[0]) for x, y in status_groups]
376
377 reviewers.append((obj, obj.user, obj.reasons, obj.mandatory, statuses))
378
379 return reviewers
380
357 381 def reviewers_statuses(self, pull_request):
358 382 _commit_statuses = self.get_statuses(
359 383 pull_request.source_repo,
360 384 pull_request=pull_request,
361 385 with_revisions=True)
362 386
363 commit_statuses = collections.defaultdict(list)
364 for st in _commit_statuses:
365 commit_statuses[st.author.username] += [st]
366
367 pull_request_reviewers = []
368
369 def version(commit_status):
370 return commit_status.version
371
372 for obj in pull_request.reviewers:
373 if not obj.user:
374 continue
375 statuses = commit_statuses.get(obj.user.username, None)
376 if statuses:
377 status_groups = itertools.groupby(
378 sorted(statuses, key=version), version)
379 statuses = [(x, list(y)[0]) for x, y in status_groups]
380
381 pull_request_reviewers.append(
382 (obj, obj.user, obj.reasons, obj.mandatory, statuses))
383
384 return pull_request_reviewers
387 return self.aggregate_votes_by_user(_commit_statuses, pull_request.reviewers)
385 388
386 389 def calculated_review_status(self, pull_request, reviewers_statuses=None):
387 390 """
@@ -228,6 +228,14 b' class CommentsModel(BaseModel):'
228 228
229 229 return todos
230 230
231 def get_commit_inline_comments(self, commit_id):
232 inline_comments = Session().query(ChangesetComment) \
233 .filter(ChangesetComment.line_no != None) \
234 .filter(ChangesetComment.f_path != None) \
235 .filter(ChangesetComment.revision == commit_id)
236 inline_comments = inline_comments.all()
237 return inline_comments
238
231 239 def _log_audit_action(self, action, action_data, auth_user, comment):
232 240 audit_logger.store(
233 241 action=action,
@@ -55,3 +55,16 b''
55 55 margin: 0 auto 35px auto;
56 56 }
57 57 }
58
59 .alert-text-success {
60 color: @alert1;
61
62 }
63
64 .alert-text-error {
65 color: @alert2;
66 }
67
68 .alert-text-warning {
69 color: @alert3;
70 }
@@ -254,7 +254,7 b' input[type="button"] {'
254 254
255 255 .btn-group-actions {
256 256 position: relative;
257 z-index: 100;
257 z-index: 50;
258 258
259 259 &:not(.open) .btn-action-switcher-container {
260 260 display: none;
@@ -1078,10 +1078,16 b' input.filediff-collapse-state {'
1078 1078 background: @color5;
1079 1079 color: white;
1080 1080 }
1081
1081 1082 &[op="comments"] { /* comments on file */
1082 1083 background: @grey4;
1083 1084 color: white;
1084 1085 }
1086
1087 &[op="options"] { /* context menu */
1088 background: @grey6;
1089 color: black;
1090 }
1085 1091 }
1086 1092 }
1087 1093
@@ -31,6 +31,10 b' a { cursor: pointer; }'
31 31 clear: both;
32 32 }
33 33
34 .display-none {
35 display: none;
36 }
37
34 38 .pull-right {
35 39 float: right !important;
36 40 }
@@ -83,6 +83,11 b' body {'
83 83 }
84 84 }
85 85
86 .flex-container {
87 display: flex;
88 justify-content: space-between;
89 }
90
86 91 .action-link{
87 92 margin-left: @padding;
88 93 padding-left: @padding;
@@ -482,6 +487,15 b' ul.auth_plugins {'
482 487 text-align: left;
483 488 overflow: hidden;
484 489 white-space: pre-line;
490 padding-top: 5px
491 }
492
493 #add_reviewer {
494 padding-top: 10px;
495 }
496
497 #add_reviewer_input {
498 padding-top: 10px
485 499 }
486 500
487 501 .pr-details-title-author-pref {
@@ -1169,9 +1183,12 b' label {'
1169 1183 a {
1170 1184 color: @grey5
1171 1185 }
1172 @media screen and (max-width: 1200px) {
1186
1187 // 1024px or smaller
1188 @media screen and (max-width: 1180px) {
1173 1189 display: none;
1174 1190 }
1191
1175 1192 }
1176 1193
1177 1194 img {
@@ -1553,6 +1570,7 b' table.integrations {'
1553 1570 width: 16px;
1554 1571 padding: 0;
1555 1572 color: black;
1573 cursor: pointer;
1556 1574 }
1557 1575
1558 1576 .reviewer_member_mandatory_remove {
@@ -1682,7 +1700,7 b' table.group_members {'
1682 1700 }
1683 1701
1684 1702 .reviewer_ac .ac-input {
1685 width: 92%;
1703 width: 100%;
1686 1704 margin-bottom: 1em;
1687 1705 }
1688 1706
@@ -2756,7 +2774,7 b' table.rctable td.td-search-results div {'
2756 2774 }
2757 2775
2758 2776 #help_kb .modal-content{
2759 max-width: 750px;
2777 max-width: 800px;
2760 2778 margin: 10% auto;
2761 2779
2762 2780 table{
@@ -3053,4 +3071,141 b' form.markup-form {'
3053 3071
3054 3072 .pr-hovercard-title {
3055 3073 padding-top: 5px;
3056 } No newline at end of file
3074 }
3075
3076 .action-divider {
3077 opacity: 0.5;
3078 }
3079
3080 .details-inline-block {
3081 display: inline-block;
3082 position: relative;
3083 }
3084
3085 .details-inline-block summary {
3086 list-style: none;
3087 }
3088
3089 details:not([open]) > :not(summary) {
3090 display: none !important;
3091 }
3092
3093 .details-reset > summary {
3094 list-style: none;
3095 }
3096
3097 .details-reset > summary::-webkit-details-marker {
3098 display: none;
3099 }
3100
3101 .details-dropdown {
3102 position: absolute;
3103 top: 100%;
3104 width: 185px;
3105 list-style: none;
3106 background-color: #fff;
3107 background-clip: padding-box;
3108 border: 1px solid @grey5;
3109 box-shadow: 0 8px 24px rgba(149, 157, 165, .2);
3110 left: -150px;
3111 text-align: left;
3112 z-index: 90;
3113 }
3114
3115 .dropdown-divider {
3116 display: block;
3117 height: 0;
3118 margin: 8px 0;
3119 border-top: 1px solid @grey5;
3120 }
3121
3122 .dropdown-item {
3123 display: block;
3124 padding: 4px 8px 4px 16px;
3125 overflow: hidden;
3126 text-overflow: ellipsis;
3127 white-space: nowrap;
3128 font-weight: normal;
3129 }
3130
3131 .right-sidebar {
3132 position: fixed;
3133 top: 0px;
3134 bottom: 0;
3135 right: 0;
3136
3137 background: #fafafa;
3138 z-index: 50;
3139 }
3140
3141 .right-sidebar {
3142 border-left: 1px solid @grey5;
3143 }
3144
3145 .right-sidebar.right-sidebar-expanded {
3146 width: 300px;
3147 overflow: scroll;
3148 }
3149
3150 .right-sidebar.right-sidebar-collapsed {
3151 width: 40px;
3152 padding: 0;
3153 display: block;
3154 overflow: hidden;
3155 }
3156
3157 .sidenav {
3158 float: right;
3159 will-change: min-height;
3160 background: #fafafa;
3161 width: 100%;
3162 }
3163
3164 .sidebar-toggle {
3165 height: 30px;
3166 text-align: center;
3167 margin: 15px 0px 0 0;
3168 }
3169
3170 .sidebar-toggle a {
3171
3172 }
3173
3174 .sidebar-content {
3175 margin-left: 15px;
3176 margin-right: 15px;
3177 }
3178
3179 .sidebar-heading {
3180 font-size: 1.2em;
3181 font-weight: 700;
3182 margin-top: 10px;
3183 }
3184
3185 .sidebar-element {
3186 margin-top: 20px;
3187 }
3188
3189 .right-sidebar-collapsed-state {
3190 display: flex;
3191 flex-direction: column;
3192 justify-content: center;
3193 align-items: center;
3194 padding: 0 10px;
3195 cursor: pointer;
3196 font-size: 1.3em;
3197 margin: 0 -15px;
3198 }
3199
3200 .right-sidebar-collapsed-state:hover {
3201 background-color: @grey5;
3202 }
3203
3204 .old-comments-marker {
3205 text-align: left;
3206 }
3207
3208 .old-comments-marker td {
3209 padding-top: 15px;
3210 border-bottom: 1px solid @grey5;
3211 }
@@ -790,7 +790,7 b' input {'
790 790
791 791 &.main_filter_input {
792 792 padding: 5px 10px;
793 min-width: 340px;
793
794 794 color: @grey7;
795 795 background: @black;
796 796 min-height: 18px;
@@ -800,11 +800,34 b' input {'
800 800 color: @grey2 !important;
801 801 background: white !important;
802 802 }
803
803 804 &:focus {
804 805 color: @grey2 !important;
805 806 background: white !important;
806 807 }
808
809 min-width: 360px;
810
811 @media screen and (max-width: 1600px) {
812 min-width: 300px;
807 813 }
814 @media screen and (max-width: 1500px) {
815 min-width: 280px;
816 }
817 @media screen and (max-width: 1400px) {
818 min-width: 260px;
819 }
820 @media screen and (max-width: 1300px) {
821 min-width: 240px;
822 }
823 @media screen and (max-width: 1200px) {
824 min-width: 220px;
825 }
826 @media screen and (max-width: 720px) {
827 min-width: 140px;
828 }
829 }
830
808 831 }
809 832
810 833
@@ -168,6 +168,7 b''
168 168 .icon-remove:before { content: '\e810'; } /* '' */
169 169 .icon-fork:before { content: '\e811'; } /* 'ξ ‘' */
170 170 .icon-more:before { content: '\e812'; } /* 'ξ ’' */
171 .icon-options:before { content: '\e812'; } /* 'ξ ’' */
171 172 .icon-search:before { content: '\e813'; } /* 'ξ “' */
172 173 .icon-scissors:before { content: '\e814'; } /* 'ξ ”' */
173 174 .icon-download:before { content: '\e815'; } /* 'ξ •' */
@@ -251,6 +252,7 b''
251 252 // TRANSFORM
252 253 .icon-merge:before {transform: rotate(180deg);}
253 254 .icon-wide-mode:before {transform: rotate(90deg);}
255 .icon-options:before {transform: rotate(90deg);}
254 256
255 257 // -- END ICON CLASSES -- //
256 258
@@ -131,6 +131,11 b' function setRCMouseBindings(repoName, re'
131 131 window.location = pyroutes.url(
132 132 'edit_repo_perms', {'repo_name': repoName});
133 133 });
134 Mousetrap.bind(['t s'], function(e) {
135 if (window.toggleSidebar !== undefined) {
136 window.toggleSidebar();
137 }
138 });
134 139 }
135 140 }
136 141
@@ -35,4 +35,75 b' var quick_repo_menu = function() {'
35 35 }, function() {
36 36 hide_quick_repo_menus();
37 37 });
38 }; No newline at end of file
38 };
39
40
41 window.toggleElement = function (elem, target) {
42 var $elem = $(elem);
43 var $target = $(target);
44
45 if ($target.is(':visible') || $target.length === 0) {
46 $target.hide();
47 $elem.html($elem.data('toggleOn'))
48 } else {
49 $target.show();
50 $elem.html($elem.data('toggleOff'))
51 }
52
53 return false
54 }
55
56 var marginExpVal = '300' // needs a sync with `.right-sidebar.right-sidebar-expanded` value
57 var marginColVal = '40' // needs a sync with `.right-sidebar.right-sidebar-collapsed` value
58
59 var marginExpanded = {'margin': '0 {0}px 0 0'.format(marginExpVal)};
60 var marginCollapsed = {'margin': '0 {0}px 0 0'.format(marginColVal)};
61
62 var updateStickyHeader = function () {
63 if (window.updateSticky !== undefined) {
64 // potentially our comments change the active window size, so we
65 // notify sticky elements
66 updateSticky()
67 }
68 }
69
70 var expandSidebar = function () {
71 var $sideBar = $('.right-sidebar');
72 $('.outerwrapper').css(marginExpanded);
73 $('.sidebar-toggle a').html('<i class="icon-right" style="margin-right: -10px"></i><i class="icon-right"></i>');
74 $('.right-sidebar-collapsed-state').hide();
75 $('.right-sidebar-expanded-state').show();
76 $('.branding').addClass('display-none');
77 $sideBar.addClass('right-sidebar-expanded')
78 $sideBar.removeClass('right-sidebar-collapsed')
79 }
80
81 var collapseSidebar = function () {
82 var $sideBar = $('.right-sidebar');
83 $('.outerwrapper').css(marginCollapsed);
84 $('.sidebar-toggle a').html('<i class="icon-left" style="margin-right: -10px"></i><i class="icon-left"></i>');
85 $('.right-sidebar-collapsed-state').show();
86 $('.right-sidebar-expanded-state').hide();
87 $('.branding').removeClass('display-none');
88 $sideBar.removeClass('right-sidebar-expanded')
89 $sideBar.addClass('right-sidebar-collapsed')
90 }
91
92 window.toggleSidebar = function () {
93 var $sideBar = $('.right-sidebar');
94
95 if ($sideBar.hasClass('right-sidebar-expanded')) {
96 // expanded -> collapsed transition
97 collapseSidebar();
98 var sidebarState = 'collapsed';
99
100 } else {
101 // collapsed -> expanded
102 expandSidebar();
103 var sidebarState = 'expanded';
104 }
105
106 // update our other sticky header in same context
107 updateStickyHeader();
108 storeUserSessionAttr('rc_user_session_attr.sidebarState', sidebarState);
109 }
@@ -279,8 +279,11 b' ReviewersController = function () {'
279 279 $('#user').show(); // show user autocomplete after load
280 280
281 281 var commitElements = data["diff_info"]['commits'];
282
282 283 if (commitElements.length === 0) {
283 prButtonLock(true, _gettext('no commits'), 'all');
284 var noCommitsMsg = '<span class="alert-text-warning">{0}</span>'.format(
285 _gettext('There are no commits to merge.'));
286 prButtonLock(true, noCommitsMsg, 'all');
284 287
285 288 } else {
286 289 // un-lock PR button, so we cannot send PR before it's calculated
@@ -324,7 +327,6 b' ReviewersController = function () {'
324 327 };
325 328
326 329 this.addReviewMember = function (reviewer_obj, reasons, mandatory) {
327 var members = self.$reviewMembers.get(0);
328 330 var id = reviewer_obj.user_id;
329 331 var username = reviewer_obj.username;
330 332
@@ -333,10 +335,10 b' ReviewersController = function () {'
333 335
334 336 // register IDS to check if we don't have this ID already in
335 337 var currentIds = [];
336 var _els = self.$reviewMembers.find('li').toArray();
337 for (el in _els) {
338 currentIds.push(_els[el].id)
339 }
338
339 $.each(self.$reviewMembers.find('.reviewer_entry'), function (index, value) {
340 currentIds.push($(value).data('reviewerUserId'))
341 })
340 342
341 343 var userAllowedReview = function (userId) {
342 344 var allowed = true;
@@ -354,12 +356,12 b' ReviewersController = function () {'
354 356 alert(_gettext('User `{0}` not allowed to be a reviewer').format(username));
355 357 } else {
356 358 // only add if it's not there
357 var alreadyReviewer = currentIds.indexOf('reviewer_' + id) != -1;
359 var alreadyReviewer = currentIds.indexOf(id) != -1;
358 360
359 361 if (alreadyReviewer) {
360 362 alert(_gettext('User `{0}` already in reviewers').format(username));
361 363 } else {
362 members.innerHTML += renderTemplate('reviewMemberEntry', {
364 var reviewerEntry = renderTemplate('reviewMemberEntry', {
363 365 'member': reviewer_obj,
364 366 'mandatory': mandatory,
365 367 'reasons': reasons,
@@ -368,7 +370,9 b' ReviewersController = function () {'
368 370 'review_status_label': _gettext('Not Reviewed'),
369 371 'user_group': reviewer_obj.user_group,
370 372 'create': true,
371 });
373 'rule_show': true,
374 })
375 $(self.$reviewMembers.selector).append(reviewerEntry);
372 376 tooltipActivate();
373 377 }
374 378 }
@@ -492,7 +496,7 b' var ReviewerAutoComplete = function(inpu'
492 496 };
493 497
494 498
495 VersionController = function () {
499 window.VersionController = function () {
496 500 var self = this;
497 501 this.$verSource = $('input[name=ver_source]');
498 502 this.$verTarget = $('input[name=ver_target]');
@@ -612,25 +616,10 b' VersionController = function () {'
612 616 return false
613 617 };
614 618
615 this.toggleElement = function (elem, target) {
616 var $elem = $(elem);
617 var $target = $(target);
618
619 if ($target.is(':visible') || $target.length === 0) {
620 $target.hide();
621 $elem.html($elem.data('toggleOn'))
622 } else {
623 $target.show();
624 $elem.html($elem.data('toggleOff'))
625 }
626
627 return false
628 }
629
630 619 };
631 620
632 621
633 UpdatePrController = function () {
622 window.UpdatePrController = function () {
634 623 var self = this;
635 624 this.$updateCommits = $('#update_commits');
636 625 this.$updateCommitsSwitcher = $('#update_commits_switcher');
@@ -672,4 +661,230 b' UpdatePrController = function () {'
672 661 templateContext.repo_name,
673 662 templateContext.pull_request_data.pull_request_id, force);
674 663 };
675 }; No newline at end of file
664 };
665
666 /**
667 * Reviewer display panel
668 */
669 window.ReviewersPanel = {
670 editButton: null,
671 closeButton: null,
672 addButton: null,
673 removeButtons: null,
674 reviewRules: null,
675 setReviewers: null,
676
677 setSelectors: function () {
678 var self = this;
679 self.editButton = $('#open_edit_reviewers');
680 self.closeButton =$('#close_edit_reviewers');
681 self.addButton = $('#add_reviewer');
682 self.removeButtons = $('.reviewer_member_remove,.reviewer_member_mandatory_remove');
683 },
684
685 init: function (reviewRules, setReviewers) {
686 var self = this;
687 self.setSelectors();
688
689 this.reviewRules = reviewRules;
690 this.setReviewers = setReviewers;
691
692 this.editButton.on('click', function (e) {
693 self.edit();
694 });
695 this.closeButton.on('click', function (e) {
696 self.close();
697 self.renderReviewers();
698 });
699
700 self.renderReviewers();
701
702 },
703
704 renderReviewers: function () {
705
706 $('#review_members').html('')
707 $.each(this.setReviewers.reviewers, function (key, val) {
708 var member = val;
709
710 var entry = renderTemplate('reviewMemberEntry', {
711 'member': member,
712 'mandatory': member.mandatory,
713 'reasons': member.reasons,
714 'allowed_to_update': member.allowed_to_update,
715 'review_status': member.review_status,
716 'review_status_label': member.review_status_label,
717 'user_group': member.user_group,
718 'create': false
719 });
720
721 $('#review_members').append(entry)
722 });
723 tooltipActivate();
724
725 },
726
727 edit: function (event) {
728 this.editButton.hide();
729 this.closeButton.show();
730 this.addButton.show();
731 $(this.removeButtons.selector).css('visibility', 'visible');
732 // review rules
733 reviewersController.loadReviewRules(this.reviewRules);
734 },
735
736 close: function (event) {
737 this.editButton.show();
738 this.closeButton.hide();
739 this.addButton.hide();
740 $(this.removeButtons.selector).css('visibility', 'hidden');
741 // hide review rules
742 reviewersController.hideReviewRules()
743 }
744 };
745
746
747 /**
748 * OnLine presence using channelstream
749 */
750 window.ReviewerPresenceController = function (channel) {
751 var self = this;
752 this.channel = channel;
753 this.users = {};
754
755 this.storeUsers = function (users) {
756 self.users = {}
757 $.each(users, function (index, value) {
758 var userId = value.state.id;
759 self.users[userId] = value.state;
760 })
761 }
762
763 this.render = function () {
764 $.each($('.reviewer_entry'), function (index, value) {
765 var userData = $(value).data();
766 if (self.users[userData.reviewerUserId] !== undefined) {
767 $(value).find('.presence-state').show();
768 } else {
769 $(value).find('.presence-state').hide();
770 }
771 })
772 };
773
774 this.handlePresence = function (data) {
775 if (data.type == 'presence' && data.channel === self.channel) {
776 this.storeUsers(data.users);
777 this.render()
778 }
779 };
780
781 this.handleChannelUpdate = function (data) {
782 if (data.channel === this.channel) {
783 this.storeUsers(data.state.users);
784 this.render()
785 }
786
787 };
788
789 /* subscribe to the current presence */
790 $.Topic('/connection_controller/presence').subscribe(this.handlePresence.bind(this));
791 /* subscribe to updates e.g connect/disconnect */
792 $.Topic('/connection_controller/channel_update').subscribe(this.handleChannelUpdate.bind(this));
793
794 };
795
796 window.refreshComments = function (version) {
797 version = version || templateContext.pull_request_data.pull_request_version || '';
798
799 // Pull request case
800 if (templateContext.pull_request_data.pull_request_id !== null) {
801 var params = {
802 'pull_request_id': templateContext.pull_request_data.pull_request_id,
803 'repo_name': templateContext.repo_name,
804 'version': version,
805 };
806 var loadUrl = pyroutes.url('pullrequest_comments', params);
807 } // commit case
808 else {
809 return
810 }
811
812 var currentIDs = []
813 $.each($('.comment'), function (idx, element) {
814 currentIDs.push($(element).data('commentId'));
815 });
816 var data = {"comments[]": currentIDs};
817
818 var $targetElem = $('.comments-content-table');
819 $targetElem.css('opacity', 0.3);
820 $targetElem.load(
821 loadUrl, data, function (responseText, textStatus, jqXHR) {
822 if (jqXHR.status !== 200) {
823 return false;
824 }
825 var $counterElem = $('#comments-count');
826 var newCount = $(responseText).data('counter');
827 if (newCount !== undefined) {
828 var callback = function () {
829 $counterElem.animate({'opacity': 1.00}, 200)
830 $counterElem.html(newCount);
831 };
832 $counterElem.animate({'opacity': 0.15}, 200, callback);
833 }
834
835 $targetElem.css('opacity', 1);
836 tooltipActivate();
837 }
838 );
839 }
840
841 window.refreshTODOs = function (version) {
842 version = version || templateContext.pull_request_data.pull_request_version || '';
843 // Pull request case
844 if (templateContext.pull_request_data.pull_request_id !== null) {
845 var params = {
846 'pull_request_id': templateContext.pull_request_data.pull_request_id,
847 'repo_name': templateContext.repo_name,
848 'version': version,
849 };
850 var loadUrl = pyroutes.url('pullrequest_comments', params);
851 } // commit case
852 else {
853 return
854 }
855
856 var currentIDs = []
857 $.each($('.comment'), function (idx, element) {
858 currentIDs.push($(element).data('commentId'));
859 });
860
861 var data = {"comments[]": currentIDs};
862 var $targetElem = $('.todos-content-table');
863 $targetElem.css('opacity', 0.3);
864 $targetElem.load(
865 loadUrl, data, function (responseText, textStatus, jqXHR) {
866 if (jqXHR.status !== 200) {
867 return false;
868 }
869 var $counterElem = $('#todos-count')
870 var newCount = $(responseText).data('counter');
871 if (newCount !== undefined) {
872 var callback = function () {
873 $counterElem.animate({'opacity': 1.00}, 200)
874 $counterElem.html(newCount);
875 };
876 $counterElem.animate({'opacity': 0.15}, 200, callback);
877 }
878
879 $targetElem.css('opacity', 1);
880 tooltipActivate();
881 }
882 );
883 }
884
885 window.refreshAllComments = function (version) {
886 version = version || templateContext.pull_request_data.pull_request_version || '';
887
888 refreshComments(version);
889 refreshTODOs(version);
890 };
@@ -701,9 +701,6 b''
701 701 notice_messages, notice_level = c.rhodecode_user.get_notice_messages()
702 702 notice_display = 'none' if len(notice_messages) == 0 else ''
703 703 %>
704 <style>
705
706 </style>
707 704
708 705 <ul id="quick" class="main_nav navigation horizontal-list">
709 706 ## notice box for important system messages
@@ -1202,6 +1199,7 b''
1202 1199 ('g p', 'Goto pull requests page'),
1203 1200 ('g o', 'Goto repository settings'),
1204 1201 ('g O', 'Goto repository access permissions settings'),
1202 ('t s', 'Toggle sidebar on some pages'),
1205 1203 ]
1206 1204 %>
1207 1205 %for key, desc in elems:
@@ -1221,3 +1219,36 b''
1221 1219 </div><!-- /.modal-content -->
1222 1220 </div><!-- /.modal-dialog -->
1223 1221 </div><!-- /.modal -->
1222
1223
1224 <script type="text/javascript">
1225 (function () {
1226 "use sctrict";
1227
1228 var $sideBar = $('.right-sidebar');
1229 var expanded = $sideBar.hasClass('right-sidebar-expanded');
1230 var sidebarState = templateContext.session_attrs.sidebarState;
1231 var sidebarEnabled = $('aside.right-sidebar').get(0);
1232
1233 if (sidebarState === 'expanded') {
1234 expanded = true
1235 } else if (sidebarState === 'collapsed') {
1236 expanded = false
1237 }
1238 if (sidebarEnabled) {
1239 // show sidebar since it's hidden on load
1240 $('.right-sidebar').show();
1241
1242 // init based on set initial class, or if defined user session attrs
1243 if (expanded) {
1244 window.expandSidebar();
1245 window.updateStickyHeader();
1246
1247 } else {
1248 window.collapseSidebar();
1249 window.updateStickyHeader();
1250 }
1251 }
1252 })()
1253
1254 </script>
@@ -4,6 +4,8 b''
4 4 <%namespace name="base" file="/base/base.mako"/>
5 5 <%namespace name="diff_block" file="/changeset/diff_block.mako"/>
6 6 <%namespace name="file_base" file="/files/base.mako"/>
7 <%namespace name="sidebar" file="/base/sidebar.mako"/>
8
7 9
8 10 <%def name="title()">
9 11 ${_('{} Commit').format(c.repo_name)} - ${h.show_id(c.commit)}
@@ -100,22 +102,6 b''
100 102 % endif
101 103 </div>
102 104
103 %if c.statuses:
104 <div class="tag status-tag-${c.statuses[0]} pull-right">
105 <i class="icon-circle review-status-${c.statuses[0]}"></i>
106 <div class="pull-right">${h.commit_status_lbl(c.statuses[0])}</div>
107 </div>
108 %endif
109
110 </div>
111
112 </div>
113 </div>
114
115 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
116 <div class="left-label-summary">
117 <p>${_('Commit navigation')}:</p>
118 <div class="right-label-summary">
119 105 <span id="parent_link" class="tag tagtag">
120 106 <a href="#parentCommit" title="${_('Parent Commit')}"><i class="icon-left icon-no-margin"></i>${_('parent')}</a>
121 107 </span>
@@ -123,7 +109,9 b''
123 109 <span id="child_link" class="tag tagtag">
124 110 <a href="#childCommit" title="${_('Child Commit')}">${_('child')}<i class="icon-right icon-no-margin"></i></a>
125 111 </span>
112
126 113 </div>
114
127 115 </div>
128 116 </div>
129 117
@@ -160,7 +148,9 b''
160 148 <%namespace name="cbdiffs" file="/codeblocks/diffs.mako"/>
161 149 ${cbdiffs.render_diffset_menu(c.changes[c.commit.raw_id], commit=c.commit)}
162 150 ${cbdiffs.render_diffset(
163 c.changes[c.commit.raw_id], commit=c.commit, use_comments=True,inline_comments=c.inline_comments )}
151 c.changes[c.commit.raw_id], commit=c.commit, use_comments=True,
152 inline_comments=c.inline_comments,
153 show_todos=False)}
164 154 </div>
165 155
166 156 ## template for inline comment form
@@ -169,7 +159,7 b''
169 159 ## comments heading with count
170 160 <div class="comments-heading">
171 161 <i class="icon-comment"></i>
172 ${_('Comments')} ${len(c.comments)}
162 ${_('General Comments')} ${len(c.comments)}
173 163 </div>
174 164
175 165 ## render comments
@@ -180,12 +170,130 b''
180 170 h.commit_status(c.rhodecode_db_repo, c.commit.raw_id))}
181 171 </div>
182 172
173 ### NAV SIDEBAR
174 <aside class="right-sidebar right-sidebar-expanded" id="commit-nav-sticky" style="display: none">
175 <div class="sidenav navbar__inner" >
176 ## TOGGLE
177 <div class="sidebar-toggle" onclick="toggleSidebar(); return false">
178 <a href="#toggleSidebar" class="grey-link-action">
179
180 </a>
181 </div>
182
183 ## CONTENT
184 <div class="sidebar-content">
185
186 ## RULES SUMMARY/RULES
187 <div class="sidebar-element clear-both">
188 <% vote_title = _ungettext(
189 'Status calculated based on votes from {} reviewer',
190 'Status calculated based on votes from {} reviewers', len(c.allowed_reviewers)).format(len(c.allowed_reviewers))
191 %>
192
193 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${vote_title}">
194 <i class="icon-circle review-status-${c.commit_review_status}"></i>
195 ${len(c.allowed_reviewers)}
196 </div>
197 </div>
198
199 ## REVIEWERS
200 <div class="right-sidebar-expanded-state pr-details-title">
201 <span class="tooltip sidebar-heading" title="${vote_title}">
202 <i class="icon-circle review-status-${c.commit_review_status}"></i>
203 ${_('Reviewers')}
204 </span>
205 </div>
206
207 <div id="reviewers" class="right-sidebar-expanded-state pr-details-content reviewers">
208
209 <table id="review_members" class="group_members">
210 ## This content is loaded via JS and ReviewersPanel
211 </table>
212
213 </div>
214
215 ## TODOs
216 <div class="sidebar-element clear-both">
217 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="TODOs">
218 <i class="icon-flag-filled"></i>
219 <span id="todos-count">${len(c.unresolved_comments)}</span>
220 </div>
221
222 <div class="right-sidebar-expanded-state pr-details-title">
223 ## Only show unresolved, that is only what matters
224 <span class="sidebar-heading noselect" onclick="refreshTODOs(); return false">
225 <i class="icon-flag-filled"></i>
226 TODOs
227 </span>
228
229 % if c.resolved_comments:
230 <span class="block-right action_button last-item noselect" onclick="$('.unresolved-todo-text').toggle(); return toggleElement(this, '.resolved-todo');" data-toggle-on="Show resolved" data-toggle-off="Hide resolved">Show resolved</span>
231 % else:
232 <span class="block-right last-item noselect">Show resolved</span>
233 % endif
234
235 </div>
236
237 <div class="right-sidebar-expanded-state pr-details-content">
238 % if c.unresolved_comments + c.resolved_comments:
239 ${sidebar.comments_table(c.unresolved_comments + c.resolved_comments, len(c.unresolved_comments), todo_comments=True, is_pr=False)}
240 % else:
241 <table>
242 <tr>
243 <td>
244 ${_('No TODOs yet')}
245 </td>
246 </tr>
247 </table>
248 % endif
249 </div>
250 </div>
251
252 ## COMMENTS
253 <div class="sidebar-element clear-both">
254 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Comments')}">
255 <i class="icon-comment" style="color: #949494"></i>
256 <span id="comments-count">${len(c.inline_comments_flat+c.comments)}</span>
257 <span class="display-none" id="general-comments-count">${len(c.comments)}</span>
258 <span class="display-none" id="inline-comments-count">${len(c.inline_comments_flat)}</span>
259 </div>
260
261 <div class="right-sidebar-expanded-state pr-details-title">
262 <span class="sidebar-heading noselect" onclick="refreshComments(); return false">
263 <i class="icon-comment" style="color: #949494"></i>
264 ${_('Comments')}
265 </span>
266
267 </div>
268
269 <div class="right-sidebar-expanded-state pr-details-content">
270 % if c.inline_comments_flat + c.comments:
271 ${sidebar.comments_table(c.inline_comments_flat + c.comments, len(c.inline_comments_flat+c.comments), is_pr=False)}
272 % else:
273 <table>
274 <tr>
275 <td>
276 ${_('No Comments yet')}
277 </td>
278 </tr>
279 </table>
280 % endif
281 </div>
282
283 </div>
284
285 </div>
286
287 </div>
288 </aside>
289
183 290 ## FORM FOR MAKING JS ACTION AS CHANGESET COMMENTS
184 291 <script type="text/javascript">
292 window.setReviewersData = ${c.commit_set_reviewers_data_json | n};
185 293
186 294 $(document).ready(function() {
295 var boxmax = parseInt($('#trimmed_message_box').css('max-height'), 10);
187 296
188 var boxmax = parseInt($('#trimmed_message_box').css('max-height'), 10);
189 297 if($('#trimmed_message_box').height() === boxmax){
190 298 $('#message_expand').show();
191 299 }
@@ -225,9 +333,11 b''
225 333 }
226 334 if(data.results.length === 1){
227 335 var commit = data.results[0];
228 window.location = pyroutes.url('repo_commit', {'repo_name': '${c.repo_name}','commit_id': commit.raw_id});
229 }
230 else if(data.results.length === 2){
336 window.location = pyroutes.url('repo_commit', {
337 'repo_name': '${c.repo_name}',
338 'commit_id': commit.raw_id
339 });
340 } else if (data.results.length === 2) {
231 341 $('#child_link').addClass('disabled');
232 342 $('#child_link').addClass('double');
233 343
@@ -236,13 +346,19 b''
236 346 .replace('__branch__', data.results[0].branch)
237 347 .replace('__rev__','r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0,6)))
238 348 .replace('__title__', data.results[0].message)
239 .replace('__url__', pyroutes.url('repo_commit', {'repo_name': '${c.repo_name}','commit_id': data.results[0].raw_id}));
349 .replace('__url__', pyroutes.url('repo_commit', {
350 'repo_name': '${c.repo_name}',
351 'commit_id': data.results[0].raw_id
352 }));
240 353 _html +=' | ';
241 354 _html +='<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a> '
242 355 .replace('__branch__', data.results[1].branch)
243 356 .replace('__rev__','r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0,6)))
244 357 .replace('__title__', data.results[1].message)
245 .replace('__url__', pyroutes.url('repo_commit', {'repo_name': '${c.repo_name}','commit_id': data.results[1].raw_id}));
358 .replace('__url__', pyroutes.url('repo_commit', {
359 'repo_name': '${c.repo_name}',
360 'commit_id': data.results[1].raw_id
361 }));
246 362 $('#child_link').html(_html);
247 363 }
248 364 }
@@ -264,9 +380,11 b''
264 380 }
265 381 if(data.results.length === 1){
266 382 var commit = data.results[0];
267 window.location = pyroutes.url('repo_commit', {'repo_name': '${c.repo_name}','commit_id': commit.raw_id});
268 }
269 else if(data.results.length === 2){
383 window.location = pyroutes.url('repo_commit', {
384 'repo_name': '${c.repo_name}',
385 'commit_id': commit.raw_id
386 });
387 } else if (data.results.length === 2) {
270 388 $('#parent_link').addClass('disabled');
271 389 $('#parent_link').addClass('double');
272 390
@@ -275,13 +393,19 b''
275 393 .replace('__branch__', data.results[0].branch)
276 394 .replace('__rev__','r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0,6)))
277 395 .replace('__title__', data.results[0].message)
278 .replace('__url__', pyroutes.url('repo_commit', {'repo_name': '${c.repo_name}','commit_id': data.results[0].raw_id}));
396 .replace('__url__', pyroutes.url('repo_commit', {
397 'repo_name': '${c.repo_name}',
398 'commit_id': data.results[0].raw_id
399 }));
279 400 _html +=' | ';
280 401 _html +='<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a>'
281 402 .replace('__branch__', data.results[1].branch)
282 403 .replace('__rev__','r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0,6)))
283 404 .replace('__title__', data.results[1].message)
284 .replace('__url__', pyroutes.url('repo_commit', {'repo_name': '${c.repo_name}','commit_id': data.results[1].raw_id}));
405 .replace('__url__', pyroutes.url('repo_commit', {
406 'repo_name': '${c.repo_name}',
407 'commit_id': data.results[1].raw_id
408 }));
285 409 $('#parent_link').html(_html);
286 410 }
287 411 }
@@ -296,6 +420,11 b''
296 420 e.preventDefault();
297 421 });
298 422
423 ReviewersPanel.init(null, setReviewersData);
424
425 var channel = '${c.commit_broadcast_channel}';
426 new ReviewerPresenceController(channel)
427
299 428 })
300 429 </script>
301 430
@@ -11,6 +11,10 b''
11 11 <%namespace name="base" file="/base/base.mako"/>
12 12 <%def name="comment_block(comment, inline=False, active_pattern_entries=None)">
13 13
14 <%
15 from rhodecode.model.comment import CommentsModel
16 comment_model = CommentsModel()
17 %>
14 18 <% comment_ver = comment.get_index_version(getattr(c, 'versions', [])) %>
15 19 <% latest_ver = len(getattr(c, 'versions', [])) %>
16 20
@@ -155,20 +159,16 b''
155 159 </div>
156 160 %endif
157 161
158 <a class="permalink" href="#comment-${comment.comment_id}">&para; #${comment.comment_id}</a>
159
160 162 <div class="comment-links-block">
161 163
162 164 % if inline:
163 165 <a class="pr-version-inline" href="${request.current_route_path(_query=dict(version=comment.pull_request_version_id), _anchor='comment-{}'.format(comment.comment_id))}">
164 166 % if outdated_at_ver:
165 <code class="tooltip pr-version-num" title="${_('Outdated comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}">
166 outdated ${'v{}'.format(comment_ver)} |
167 </code>
167 <code class="tooltip pr-version-num" title="${_('Outdated comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}">outdated ${'v{}'.format(comment_ver)}</code>
168 <code class="action-divider">|</code>
168 169 % elif comment_ver:
169 <code class="tooltip pr-version-num" title="${_('Comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}">
170 ${'v{}'.format(comment_ver)} |
171 </code>
170 <code class="tooltip pr-version-num" title="${_('Comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}">${'v{}'.format(comment_ver)}</code>
171 <code class="action-divider">|</code>
172 172 % endif
173 173 </a>
174 174 % else:
@@ -179,45 +179,70 b''
179 179 href="?version=${comment.pull_request_version_id}#comment-${comment.comment_id}"
180 180 >
181 181 ${_('Outdated comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}
182 </a> |
182 </a>
183 <code class="action-divider">|</code>
183 184 % else:
184 185 <a class="tooltip pr-version"
185 186 title="${_('Comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}"
186 187 href="${h.route_path('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id, version=comment.pull_request_version_id)}"
187 188 >
188 <code class="pr-version-num">
189 ${'v{}'.format(comment_ver)}
190 </code>
191 </a> |
189 <code class="pr-version-num">${'v{}'.format(comment_ver)}</code>
190 </a>
191 <code class="action-divider">|</code>
192 192 % endif
193 193
194 194 % endif
195 195 % endif
196 196
197 <details class="details-reset details-inline-block">
198 <summary class="noselect"><i class="icon-options cursor-pointer"></i></summary>
199 <details-menu class="details-dropdown">
200
201 <div class="dropdown-item">
202 ${_('Comment')} #${comment.comment_id}
203 <span class="pull-right icon-clipboard clipboard-action" data-clipboard-text="${comment_model.get_url(comment,request, permalink=True, anchor='comment-{}'.format(comment.comment_id))}" title="${_('Copy permalink')}"></span>
204 </div>
205
197 206 ## show delete comment if it's not a PR (regular comments) or it's PR that is not closed
198 207 ## only super-admin, repo admin OR comment owner can delete, also hide delete if currently viewed comment is outdated
199 208 %if not outdated_at_ver and (not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed())):
200 209 ## permissions to delete
201 210 %if comment.immutable is False and (c.is_super_admin or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id):
202 <a onclick="return Rhodecode.comments.editComment(this);"
203 class="edit-comment">${_('Edit')}</a>
204 | <a onclick="return Rhodecode.comments.deleteComment(this);"
205 class="delete-comment">${_('Delete')}</a>
211 <div class="dropdown-divider"></div>
212 <div class="dropdown-item">
213 <a onclick="return Rhodecode.comments.editComment(this);" class="btn btn-link btn-sm edit-comment">${_('Edit')}</a>
214 </div>
215 <div class="dropdown-item">
216 <a onclick="return Rhodecode.comments.deleteComment(this);" class="btn btn-link btn-sm btn-danger delete-comment">${_('Delete')}</a>
217 </div>
206 218 %else:
219 <div class="dropdown-divider"></div>
220 <div class="dropdown-item">
207 221 <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Edit')}</a>
208 | <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Delete')}</a>
222 </div>
223 <div class="dropdown-item">
224 <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Delete')}</a>
225 </div>
209 226 %endif
210 227 %else:
228 <div class="dropdown-divider"></div>
229 <div class="dropdown-item">
211 230 <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Edit')}</a>
212 | <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Delete')}</a>
231 </div>
232 <div class="dropdown-item">
233 <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Delete')}</a>
234 </div>
213 235 %endif
236 </details-menu>
237 </details>
214 238
239 <code class="action-divider">|</code>
215 240 % if outdated_at_ver:
216 | <a onclick="return Rhodecode.comments.prevOutdatedComment(this);" class="tooltip prev-comment" title="${_('Jump to the previous outdated comment')}"> <i class="icon-angle-left"></i> </a>
217 | <a onclick="return Rhodecode.comments.nextOutdatedComment(this);" class="tooltip next-comment" title="${_('Jump to the next outdated comment')}"> <i class="icon-angle-right"></i></a>
241 <a onclick="return Rhodecode.comments.prevOutdatedComment(this);" class="tooltip prev-comment" title="${_('Jump to the previous outdated comment')}"> <i class="icon-angle-left"></i> </a>
242 <a onclick="return Rhodecode.comments.nextOutdatedComment(this);" class="tooltip next-comment" title="${_('Jump to the next outdated comment')}"> <i class="icon-angle-right"></i></a>
218 243 % else:
219 | <a onclick="return Rhodecode.comments.prevComment(this);" class="tooltip prev-comment" title="${_('Jump to the previous comment')}"> <i class="icon-angle-left"></i></a>
220 | <a onclick="return Rhodecode.comments.nextComment(this);" class="tooltip next-comment" title="${_('Jump to the next comment')}"> <i class="icon-angle-right"></i></a>
244 <a onclick="return Rhodecode.comments.prevComment(this);" class="tooltip prev-comment" title="${_('Jump to the previous comment')}"> <i class="icon-angle-left"></i></a>
245 <a onclick="return Rhodecode.comments.nextComment(this);" class="tooltip next-comment" title="${_('Jump to the next comment')}"> <i class="icon-angle-right"></i></a>
221 246 % endif
222 247
223 248 </div>
@@ -102,6 +102,11 b''
102 102 <%namespace name="diff_block" file="/changeset/diff_block.mako"/>
103 103
104 104 %for commit in c.commit_ranges:
105 ## commit range header for each individual diff
106 <h3>
107 <a class="tooltip revision" title="${h.tooltip(commit.message)}" href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=commit.raw_id)}">${('r%s:%s' % (commit.idx,h.short_id(commit.raw_id)))}</a>
108 </h3>
109
105 110 ${cbdiffs.render_diffset_menu(c.changes[commit.raw_id])}
106 111 ${cbdiffs.render_diffset(
107 112 diffset=c.changes[commit.raw_id],
@@ -61,6 +61,8 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
61 61 diffset_container_id = h.md5(diffset.target_ref)
62 62 collapse_all = len(diffset.files) > collapse_when_files_over
63 63 active_pattern_entries = h.get_active_pattern_entries(getattr(c, 'repo_name', None))
64 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
65 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
64 66 %>
65 67
66 68 %if use_comments:
@@ -208,13 +210,6 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
208 210 <a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
209 211 </h2>
210 212 </div>
211 ## commit range header for each individual diff
212 % elif commit and hasattr(c, 'commit_ranges') and len(c.commit_ranges) > 1:
213 <div class="diffset-heading ${(diffset.limited_diff and 'diffset-heading-warning' or '')}">
214 <div class="clearinner">
215 <a class="tooltip revision" title="${h.tooltip(commit.message)}" href="${h.route_path('repo_commit',repo_name=diffset.repo_name,commit_id=commit.raw_id)}">${('r%s:%s' % (commit.idx,h.short_id(commit.raw_id)))}</a>
216 </div>
217 </div>
218 213 % endif
219 214
220 215 <div id="todo-box">
@@ -239,6 +234,43 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
239 234 <% over_lines_changed_limit = False %>
240 235 %for i, filediff in enumerate(diffset.files):
241 236
237 %if filediff.source_file_path and filediff.target_file_path:
238 %if filediff.source_file_path != filediff.target_file_path:
239 ## file was renamed, or copied
240 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
241 <%
242 final_file_name = h.literal(u'{} <i class="icon-angle-left"></i> <del>{}</del>'.format(filediff.target_file_path, filediff.source_file_path))
243 final_path = filediff.target_file_path
244 %>
245 %elif COPIED_FILENODE in filediff.patch['stats']['ops']:
246 <%
247 final_file_name = h.literal(u'{} <i class="icon-angle-left"></i> {}'.format(filediff.target_file_path, filediff.source_file_path))
248 final_path = filediff.target_file_path
249 %>
250 %endif
251 %else:
252 ## file was modified
253 <%
254 final_file_name = filediff.source_file_path
255 final_path = final_file_name
256 %>
257 %endif
258 %else:
259 %if filediff.source_file_path:
260 ## file was deleted
261 <%
262 final_file_name = filediff.source_file_path
263 final_path = final_file_name
264 %>
265 %else:
266 ## file was added
267 <%
268 final_file_name = filediff.target_file_path
269 final_path = final_file_name
270 %>
271 %endif
272 %endif
273
242 274 <%
243 275 lines_changed = filediff.patch['stats']['added'] + filediff.patch['stats']['deleted']
244 276 over_lines_changed_limit = lines_changed > lines_changed_limit
@@ -258,13 +290,39 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
258 290 total_file_comments = [_c for _c in h.itertools.chain.from_iterable(file_comments) if not _c.outdated]
259 291 %>
260 292 <div class="filediff-collapse-indicator icon-"></div>
293
294 ## Comments/Options PILL
261 295 <span class="pill-group pull-right" >
262 296 <span class="pill" op="comments">
263
264 297 <i class="icon-comment"></i> ${len(total_file_comments)}
265 298 </span>
299
300 <details class="details-reset details-inline-block">
301 <summary class="noselect">
302 <i class="pill icon-options cursor-pointer" op="options"></i>
303 </summary>
304 <details-menu class="details-dropdown">
305
306 <div class="dropdown-item">
307 <span>${final_path}</span>
308 <span class="pull-right icon-clipboard clipboard-action" data-clipboard-text="${final_path}" title="Copy file path"></span>
309 </div>
310
311 <div class="dropdown-divider"></div>
312
313 <div class="dropdown-item">
314 <% permalink = request.current_route_url(_anchor='a_{}'.format(h.FID(filediff.raw_id, filediff.patch['filename']))) %>
315 <a href="${permalink}">ΒΆ permalink</a>
316 <span class="pull-right icon-clipboard clipboard-action" data-clipboard-text="${permalink}" title="Copy permalink"></span>
317 </div>
318
319
320 </details-menu>
321 </details>
322
266 323 </span>
267 ${diff_ops(filediff)}
324
325 ${diff_ops(final_file_name, filediff)}
268 326
269 327 </label>
270 328
@@ -463,43 +521,15 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
463 521 </div>
464 522 </%def>
465 523
466 <%def name="diff_ops(filediff)">
524 <%def name="diff_ops(file_name, filediff)">
467 525 <%
468 526 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
469 527 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
470 528 %>
471 529 <span class="pill">
472 530 <i class="icon-file-text"></i>
473 %if filediff.source_file_path and filediff.target_file_path:
474 %if filediff.source_file_path != filediff.target_file_path:
475 ## file was renamed, or copied
476 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
477 ${filediff.target_file_path} β¬… <del>${filediff.source_file_path}</del>
478 <% final_path = filediff.target_file_path %>
479 %elif COPIED_FILENODE in filediff.patch['stats']['ops']:
480 ${filediff.target_file_path} β¬… ${filediff.source_file_path}
481 <% final_path = filediff.target_file_path %>
482 %endif
483 %else:
484 ## file was modified
485 ${filediff.source_file_path}
486 <% final_path = filediff.source_file_path %>
487 %endif
488 %else:
489 %if filediff.source_file_path:
490 ## file was deleted
491 ${filediff.source_file_path}
492 <% final_path = filediff.source_file_path %>
493 %else:
494 ## file was added
495 ${filediff.target_file_path}
496 <% final_path = filediff.target_file_path %>
497 %endif
498 %endif
499 <i style="color: #aaa" class="on-hover-icon icon-clipboard clipboard-action" data-clipboard-text="${final_path}" title="${_('Copy file path')}" onclick="return false;"></i>
531 ${file_name}
500 532 </span>
501 ## anchor link
502 <a class="pill filediff-anchor" href="#a_${h.FID(filediff.raw_id, filediff.patch['filename'])}">ΒΆ</a>
503 533
504 534 <span class="pill-group pull-right">
505 535
@@ -53,6 +53,14 b" var data_hovercard_url = pyroutes.url('h"
53 53 var reviewGroup = null;
54 54 var reviewGroupColor = 'transparent';
55 55 }
56 var rule_show = rule_show || false;
57
58 if (rule_show) {
59 var rule_visibility = 'table-cell';
60 } else {
61 var rule_visibility = 'none';
62 }
63
56 64 %>
57 65
58 66 <tr id="reviewer_<%= member.user_id %>" class="reviewer_entry" tooltip="Review Group" data-reviewer-user-id="<%= member.user_id %>">
@@ -98,9 +106,9 b" var data_hovercard_url = pyroutes.url('h"
98 106 </td>
99 107
100 108 <% } else { %>
101 <td>
109 <td style="text-align: right;width: 10px;">
102 110 <% if (allowed_to_update) { %>
103 <div class="reviewer_member_remove action_button" onclick="reviewersController.removeReviewMember(<%= member.user_id %>, true)" style="visibility: <%= edit_visibility %>;">
111 <div class="reviewer_member_remove" onclick="reviewersController.removeReviewMember(<%= member.user_id %>, true)" style="visibility: <%= edit_visibility %>;">
104 112 <i class="icon-remove"></i>
105 113 </div>
106 114 <% } %>
@@ -110,7 +118,7 b" var data_hovercard_url = pyroutes.url('h"
110 118 </tr>
111 119
112 120 <tr>
113 <td colspan="4" style="display: none" class="pr-user-rule-container">
121 <td colspan="4" style="display: <%= rule_visibility %>" class="pr-user-rule-container">
114 122 <input type="hidden" name="__start__" value="reviewer:mapping">
115 123
116 124 <%if (member.user_group && member.user_group.vote_rule) { %>
@@ -19,20 +19,73 b''
19 19 <div class="box">
20 20 ${h.secure_form(h.route_path('pullrequest_create', repo_name=c.repo_name, _query=request.GET.mixed()), id='pull_request_form', request=request)}
21 21
22 <div class="box pr-summary">
22 <div class="box">
23 23
24 24 <div class="summary-details block-left">
25 25
26
27 <div class="pr-details-title">
28 ${_('New pull request')}
29 </div>
30
31 26 <div class="form" style="padding-top: 10px">
32 <!-- fields -->
33 27
34 28 <div class="fields" >
35 29
30 ## COMMIT FLOW
31 <div class="field">
32 <div class="label label-textarea">
33 <label for="commit_flow">${_('Commit flow')}:</label>
34 </div>
35
36 <div class="content">
37 <div class="flex-container">
38 <div style="width: 45%;">
39 <div class="panel panel-default source-panel">
40 <div class="panel-heading">
41 <h3 class="panel-title">${_('Source repository')}</h3>
42 </div>
43 <div class="panel-body">
44 <div style="display:none">${c.rhodecode_db_repo.description}</div>
45 ${h.hidden('source_repo')}
46 ${h.hidden('source_ref')}
47
48 <div id="pr_open_message"></div>
49 </div>
50 </div>
51 </div>
52
53 <div style="width: 90px; text-align: center; padding-top: 30px">
54 <div>
55 <i class="icon-right" style="font-size: 2.2em"></i>
56 </div>
57 <div style="position: relative; top: 10px">
58 <span class="tag tag">
59 <span id="switch_base"></span>
60 </span>
61 </div>
62
63 </div>
64
65 <div style="width: 45%;">
66
67 <div class="panel panel-default target-panel">
68 <div class="panel-heading">
69 <h3 class="panel-title">${_('Target repository')}</h3>
70 </div>
71 <div class="panel-body">
72 <div style="display:none" id="target_repo_desc"></div>
73 ${h.hidden('target_repo')}
74 ${h.hidden('target_ref')}
75 <span id="target_ref_loading" style="display: none">
76 ${_('Loading refs...')}
77 </span>
78 </div>
79 </div>
80
81 </div>
82 </div>
83
84 </div>
85
86 </div>
87
88 ## TITLE
36 89 <div class="field">
37 90 <div class="label">
38 91 <label for="pullrequest_title">${_('Title')}:</label>
@@ -45,6 +98,7 b''
45 98 </p>
46 99 </div>
47 100
101 ## DESC
48 102 <div class="field">
49 103 <div class="label label-textarea">
50 104 <label for="pullrequest_desc">${_('Description')}:</label>
@@ -55,72 +109,14 b''
55 109 </div>
56 110 </div>
57 111
112 ## REVIEWERS
58 113 <div class="field">
59 114 <div class="label label-textarea">
60 <label for="commit_flow">${_('Commit flow')}:</label>
61 </div>
62
63 ## TODO: johbo: Abusing the "content" class here to get the
64 ## desired effect. Should be replaced by a proper solution.
65
66 ##ORG
67 <div class="content">
68 <strong>${_('Source repository')}:</strong>
69 ${c.rhodecode_db_repo.description}
115 <label for="pullrequest_reviewers">${_('Reviewers')}:</label>
70 116 </div>
71 117 <div class="content">
72 ${h.hidden('source_repo')}
73 ${h.hidden('source_ref')}
74 </div>
75
76 ##OTHER, most Probably the PARENT OF THIS FORK
77 <div class="content">
78 ## filled with JS
79 <div id="target_repo_desc"></div>
80 </div>
81
82 <div class="content">
83 ${h.hidden('target_repo')}
84 ${h.hidden('target_ref')}
85 <span id="target_ref_loading" style="display: none">
86 ${_('Loading refs...')}
87 </span>
88 </div>
89 </div>
90
91 <div class="field">
92 <div class="label label-textarea">
93 <label for="pullrequest_submit"></label>
94 </div>
95 <div class="input">
96 <div class="pr-submit-button">
97 <input id="pr_submit" class="btn" name="save" type="submit" value="${_('Submit Pull Request')}">
98 </div>
99 <div id="pr_open_message"></div>
100 </div>
101 </div>
102
103 <div class="pr-spacing-container"></div>
104 </div>
105 </div>
106 </div>
107 <div>
108 ## AUTHOR
109 <div class="reviewers-title block-right">
110 <div class="pr-details-title">
111 ${_('Author of this pull request')}
112 </div>
113 </div>
114 <div class="block-right pr-details-content reviewers">
115 <ul class="group_members">
116 <li>
117 ${self.gravatar_with_user(c.rhodecode_user.email, 16, tooltip=True)}
118 </li>
119 </ul>
120 </div>
121
122 118 ## REVIEW RULES
123 <div id="review_rules" style="display: none" class="reviewers-title block-right">
119 <div id="review_rules" style="display: none" class="reviewers-title">
124 120 <div class="pr-details-title">
125 121 ${_('Reviewer rules')}
126 122 </div>
@@ -130,32 +126,48 b''
130 126 </div>
131 127
132 128 ## REVIEWERS
133 <div class="reviewers-title block-right">
129 <div class="reviewers-title">
134 130 <div class="pr-details-title">
135 131 ${_('Pull request reviewers')}
136 132 <span class="calculate-reviewers"> - ${_('loading...')}</span>
137 133 </div>
138 134 </div>
139 <div id="reviewers" class="block-right pr-details-content reviewers">
135 <div id="reviewers" class="pr-details-content reviewers">
140 136 ## members goes here, filled via JS based on initial selection !
141 137 <input type="hidden" name="__start__" value="review_members:sequence">
142 <ul id="review_members" class="group_members"></ul>
138 <table id="review_members" class="group_members">
139 ## This content is loaded via JS and ReviewersPanel
140 </table>
143 141 <input type="hidden" name="__end__" value="review_members:sequence">
142
144 143 <div id="add_reviewer_input" class='ac'>
145 144 <div class="reviewer_ac">
146 145 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))}
147 146 <div id="reviewers_container"></div>
148 147 </div>
149 148 </div>
149
150 150 </div>
151 151 </div>
152 152 </div>
153 <div class="box">
154 <div>
155 ## overview pulled by ajax
156 <div id="pull_request_overview"></div>
153
154 ## SUBMIT
155 <div class="field">
156 <div class="label label-textarea">
157 <label for="pullrequest_submit"></label>
158 </div>
159 <div class="input">
160 <div class="pr-submit-button">
161 <input id="pr_submit" class="btn" name="save" type="submit" value="${_('Submit Pull Request')}">
157 162 </div>
158 163 </div>
164 </div>
165 </div>
166 </div>
167 </div>
168
169 </div>
170
159 171 ${h.end_form()}
160 172 </div>
161 173
@@ -243,8 +255,6 b''
243 255
244 256 var diffDataHandler = function(data) {
245 257
246 $('#pull_request_overview').html(data);
247
248 258 var commitElements = data['commits'];
249 259 var files = data['files'];
250 260 var added = data['stats'][0]
@@ -303,27 +313,33 b''
303 313
304 314 msg += '<input type="hidden" name="__end__" value="revisions:sequence">'
305 315 msg += _ngettext(
306 'This pull requests will consist of <strong>{0} commit</strong>.',
307 'This pull requests will consist of <strong>{0} commits</strong>.',
316 'Compare summary: <strong>{0} commit</strong>',
317 'Compare summary: <strong>{0} commits</strong>',
308 318 commitElements.length).format(commitElements.length)
309 319
310 msg += '\n';
320 msg += '';
311 321 msg += _ngettext(
312 '<strong>{0} file</strong> changed, ',
313 '<strong>{0} files</strong> changed, ',
322 '<strong>, and {0} file</strong> changed.',
323 '<strong>, and {0} files</strong> changed.',
314 324 files.length).format(files.length)
315 msg += '<span class="op-added">{0} lines inserted</span>, <span class="op-deleted">{1} lines deleted</span>.'.format(added, deleted)
316 325
317 msg += '\n\n <a class="" id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url);
326 msg += '\n Diff: <span class="op-added">{0} lines inserted</span>, <span class="op-deleted">{1} lines deleted </span>.'.format(added, deleted)
327
328 msg += '\n <a class="" id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url);
318 329
319 330 if (commitElements.length) {
320 331 var commitsLink = '<a href="#pull_request_overview"><strong>{0}</strong></a>'.format(commitElements.length);
321 332 prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare');
322 333 }
323 334 else {
324 prButtonLock(true, "${_('There are no commits to merge.')}", 'compare');
335 var noCommitsMsg = '<span class="alert-text-warning">{0}</span>'.format(
336 _gettext('There are no commits to merge.'));
337 prButtonLock(true, noCommitsMsg, 'compare');
325 338 }
326 339
340 //make both panels equal
341 $('.target-panel').height($('.source-panel').height())
342
327 343 };
328 344
329 345 reviewersController = new ReviewersController();
@@ -429,10 +445,12 b''
429 445
430 446 var targetRepoChanged = function(repoData) {
431 447 // generate new DESC of target repo displayed next to select
448
449 $('#target_repo_desc').html(repoData['description']);
450
432 451 var prLink = pyroutes.url('pullrequest_new', {'repo_name': repoData['name']});
433 $('#target_repo_desc').html(
434 "<strong>${_('Target repository')}</strong>: {0}. <a href=\"{1}\">Switch base, and use as source.</a>".format(repoData['description'], prLink)
435 );
452 var title = _gettext('Switch target repository with the source.')
453 $('#switch_base').html("<a class=\"tooltip\" title=\"{0}\" href=\"{1}\">Switch sides</a>".format(title, prLink))
436 454
437 455 // generate dynamic select2 for refs.
438 456 initTargetRefs(repoData['refs']['select2_refs'],
This diff has been collapsed as it changes many lines, (547 lines changed) Show them Hide them
@@ -1,6 +1,8 b''
1 1 <%inherit file="/base/base.mako"/>
2 2 <%namespace name="base" file="/base/base.mako"/>
3 3 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
4 <%namespace name="sidebar" file="/base/sidebar.mako"/>
5
4 6
5 7 <%def name="title()">
6 8 ${_('{} Pull Request !{}').format(c.repo_name, c.pull_request.pull_request_id)}
@@ -21,113 +23,6 b''
21 23 ${self.repo_menu(active='showpullrequest')}
22 24 </%def>
23 25
24 <%def name="comments_table(comments, counter_num, todo_comments=False)">
25 <%
26 old_comments = False
27 if todo_comments:
28 cls_ = 'todos-content-table'
29 def sorter(entry):
30 user_id = entry.author.user_id
31 resolved = '1' if entry.resolved else '0'
32 if user_id == c.rhodecode_user.user_id:
33 # own comments first
34 user_id = 0
35 return '{}'.format(str(entry.comment_id).zfill(10000))
36 else:
37 cls_ = 'comments-content-table'
38 def sorter(entry):
39 user_id = entry.author.user_id
40 return '{}'.format(str(entry.comment_id).zfill(10000))
41
42
43
44 %>
45 <table class="todo-table ${cls_}" data-total-count="${len(comments)}" data-counter="${counter_num}">
46
47 % for loop_obj, comment_obj in h.looper(reversed(sorted(comments, key=sorter))):
48 <%
49 display = ''
50 _cls = ''
51 %>
52 <% comment_ver_index = comment_obj.get_index_version(getattr(c, 'versions', [])) %>
53 <%
54 prev_comment_ver_index = 0
55 if loop_obj.previous:
56 prev_comment_ver_index = loop_obj.previous.get_index_version(getattr(c, 'versions', []))
57 %>
58 <% hidden_at_ver = comment_obj.outdated_at_version_js(c.at_version_num) %>
59 <% is_from_old_ver = comment_obj.older_than_version_js(c.at_version_num) %>
60 <%
61 if (prev_comment_ver_index > comment_ver_index) and old_comments is False:
62 old_comments = True
63 %>
64 % if todo_comments:
65 % if comment_obj.resolved:
66 <% _cls = 'resolved-todo' %>
67 <% display = 'none' %>
68 % endif
69 % else:
70 ## SKIP TODOs we display them in other area
71 % if comment_obj.is_todo:
72 <% display = 'none' %>
73 % endif
74 ## Skip outdated comments
75 % if comment_obj.outdated:
76 <% display = 'none' %>
77 <% _cls = 'hidden-comment' %>
78 % endif
79 % endif
80
81 % if not todo_comments and old_comments:
82 <tr class="old-comments-marker">
83 <td colspan="3"> <code>comments from older versions</code> </td>
84 </tr>
85 ## reset markers so we only show this marker once
86 <% old_comments = None %>
87 % endif
88
89 <tr class="${_cls}" style="display: ${display};">
90 <td class="td-todo-number">
91
92 <a class="${('todo-resolved' if comment_obj.resolved else '')} permalink"
93 href="#comment-${comment_obj.comment_id}"
94 onclick="return Rhodecode.comments.scrollToComment($('#comment-${comment_obj.comment_id}'), 0, ${hidden_at_ver})">
95
96 % if todo_comments:
97 % if comment_obj.is_inline:
98 <i class="tooltip icon-code" title="Inline TODO comment ${('made in older version (v{})'.format(comment_ver_index) if is_from_old_ver == 'true' else 'made in this version')}."></i>
99 % else:
100 <i class="tooltip icon-comment" title="General TODO comment ${('made in older version (v{})'.format(comment_ver_index) if is_from_old_ver == 'true' else 'made in this version')}."></i>
101 % endif
102 % else:
103 % if comment_obj.outdated:
104 <i class="tooltip icon-comment-toggle" title="Inline Outdated made in v${comment_ver_index}."></i>
105 % elif comment_obj.is_inline:
106 <i class="tooltip icon-code" title="Inline comment ${('made in older version (v{})'.format(comment_ver_index) if is_from_old_ver == 'true' else 'made in this version')}."></i>
107 % else:
108 <i class="tooltip icon-comment" title="General comment ${('made in older version (v{})'.format(comment_ver_index) if is_from_old_ver == 'true' else 'made in this version')}."></i>
109 % endif
110 % endif
111
112 #${comment_obj.comment_id}
113 </a>
114 </td>
115
116 <td class="td-todo-gravatar">
117 ${base.gravatar(comment_obj.author.email, 16, user=comment_obj.author, tooltip=True, extra_class=['no-margin'])}
118 </td>
119 <td class="todo-comment-text-wrapper">
120 <div class="tooltip todo-comment-text timeago" title="${h.format_date(comment_obj.created_on)}" datetime="${comment_obj.created_on}${h.get_timezone(comment_obj.created_on, time_is_local=True)}">
121 <code>${h.chop_at_smart(comment_obj.text, '\n', suffix_if_chopped='...')}</code>
122 </div>
123 </td>
124 </tr>
125 % endfor
126
127 </table>
128
129 </%def>
130
131 26
132 27 <%def name="main()">
133 28 ## Container to gather extracted Tickets
@@ -140,6 +35,7 b''
140 35 // TODO: marcink switch this to pyroutes
141 36 AJAX_COMMENT_DELETE_URL = "${h.route_path('pullrequest_comment_delete',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id,comment_id='__COMMENT_ID__')}";
142 37 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
38 templateContext.pull_request_data.pull_request_version = '${request.GET.get('version', '')}';
143 39 </script>
144 40
145 41 <div class="box">
@@ -226,7 +122,7 b''
226 122
227 123 ${_('of')} <a href="${h.route_path('repo_summary', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.repo_name}</a>
228 124
229 <a class="source-details-action" href="#expand-source-details" onclick="return versionController.toggleElement(this, '.source-details')" data-toggle-on='<i class="icon-angle-down">more details</i>' data-toggle-off='<i class="icon-angle-up">less details</i>'>
125 <a class="source-details-action" href="#expand-source-details" onclick="return toggleElement(this, '.source-details')" data-toggle-on='<i class="icon-angle-down">more details</i>' data-toggle-off='<i class="icon-angle-up">less details</i>'>
230 126 <i class="icon-angle-down">more details</i>
231 127 </a>
232 128
@@ -643,101 +539,12 b''
643 539 </div>
644 540
645 541
646 ### NAVBOG RIGHT
647 <style>
648
649 .right-sidebar {
650 position: fixed;
651 top: 0px;
652 bottom: 0;
653 right: 0;
654
655 background: #fafafa;
656 z-index: 50;
657 }
658
659 .right-sidebar {
660 border-left: 1px solid #dbdbdb;
661 }
662
663 .right-sidebar.right-sidebar-expanded {
664 width: 320px;
665 overflow: scroll;
666 }
667
668 .right-sidebar.right-sidebar-collapsed {
669 width: 50px;
670 padding: 0;
671 display: block;
672 overflow: hidden;
673 }
674
675 .sidenav {
676 float: right;
677 will-change: min-height;
678 background: #fafafa;
679 width: 100%;
680 padding-top: 50px;
681 }
682
683 .sidebar-toggle {
684 height: 30px;
685 text-align: center;
686 margin: 15px 0px 0 0;
687 }
688 .sidebar-toggle a {
689
690 }
691
692 .sidebar-content {
693 margin-left: 15px;
694 margin-right: 15px;
695 }
696
697 .sidebar-heading {
698 font-size: 1.2em;
699 font-weight: 700;
700 margin-top: 10px;
701 }
702
703 .sidebar-element {
704 margin-top: 20px;
705 }
706 .right-sidebar-collapsed-state {
707 display: flex;
708 flex-direction: column;
709 justify-content: center;
710 align-items: center;
711 padding: 0 10px;
712 cursor: pointer;
713 font-size: 1.3em;
714 margin: 0 -15px;
715 }
716
717 .right-sidebar-collapsed-state:hover {
718 background-color: #dbd9da;
719 }
720
721 .old-comments-marker {
722 text-align: center;
723 }
724
725 .old-comments-marker td {
726 padding-top: 15px;
727 border-bottom: 1px solid #dbd9da;
728 }
729
730 #add_reviewer {
731 padding-top: 10px;
732 }
733
734 </style>
735
542 ### NAV SIDEBAR
736 543 <aside class="right-sidebar right-sidebar-expanded" id="pr-nav-sticky" style="display: none">
737 544 <div class="sidenav navbar__inner" >
738 545 ## TOGGLE
739 546 <div class="sidebar-toggle" onclick="toggleSidebar(); return false">
740 <a href="#toggleSidebar">
547 <a href="#toggleSidebar" class="grey-link-action">
741 548
742 549 </a>
743 550 </div>
@@ -747,8 +554,12 b''
747 554
748 555 ## RULES SUMMARY/RULES
749 556 <div class="sidebar-element clear-both">
557 <% vote_title = _ungettext(
558 'Status calculated based on votes from {} reviewer',
559 'Status calculated based on votes from {} reviewers', len(c.allowed_reviewers)).format(len(c.allowed_reviewers))
560 %>
750 561
751 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Reviewers')}">
562 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${vote_title}">
752 563 <i class="icon-circle review-status-${c.pull_request_review_status}"></i>
753 564 ${len(c.allowed_reviewers)}
754 565 </div>
@@ -769,7 +580,7 b''
769 580
770 581 ## REVIEWERS
771 582 <div class="right-sidebar-expanded-state pr-details-title">
772 <span class="tooltip sidebar-heading" title="${_ungettext('Review status calculated based on {} reviewer vote', 'Review status calculated based on {} reviewers votes', len(c.allowed_reviewers)).format(len(c.allowed_reviewers))}">
583 <span class="tooltip sidebar-heading" title="${vote_title}">
773 584 <i class="icon-circle review-status-${c.pull_request_review_status}"></i>
774 585 ${_('Reviewers')}
775 586 </span>
@@ -846,7 +657,7 b''
846 657
847 658 % if not c.at_version:
848 659 % if c.resolved_comments:
849 <span class="block-right action_button last-item noselect" onclick="$('.unresolved-todo-text').toggle(); return versionController.toggleElement(this, '.resolved-todo');" data-toggle-on="Show resolved" data-toggle-off="Hide resolved">Show resolved</span>
660 <span class="block-right action_button last-item noselect" onclick="$('.unresolved-todo-text').toggle(); return toggleElement(this, '.resolved-todo');" data-toggle-on="Show resolved" data-toggle-off="Hide resolved">Show resolved</span>
850 661 % else:
851 662 <span class="block-right last-item noselect">Show resolved</span>
852 663 % endif
@@ -863,7 +674,7 b''
863 674 </table>
864 675 % else:
865 676 % if c.unresolved_comments + c.resolved_comments:
866 ${comments_table(c.unresolved_comments + c.resolved_comments, len(c.unresolved_comments), todo_comments=True)}
677 ${sidebar.comments_table(c.unresolved_comments + c.resolved_comments, len(c.unresolved_comments), todo_comments=True)}
867 678 % else:
868 679 <table>
869 680 <tr>
@@ -882,6 +693,8 b''
882 693 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Comments')}">
883 694 <i class="icon-comment" style="color: #949494"></i>
884 695 <span id="comments-count">${len(c.inline_comments_flat+c.comments)}</span>
696 <span class="display-none" id="general-comments-count">${len(c.comments)}</span>
697 <span class="display-none" id="inline-comments-count">${len(c.inline_comments_flat)}</span>
885 698 </div>
886 699
887 700 <div class="right-sidebar-expanded-state pr-details-title">
@@ -903,7 +716,7 b''
903 716 </span>
904 717
905 718 % if outdated_comm_count_ver:
906 <span class="block-right action_button last-item noselect" onclick="return versionController.toggleElement(this, '.hidden-comment');" data-toggle-on="Show outdated" data-toggle-off="Hide outdated">Show outdated</span>
719 <span class="block-right action_button last-item noselect" onclick="return toggleElement(this, '.hidden-comment');" data-toggle-on="Show outdated" data-toggle-off="Hide outdated">Show outdated</span>
907 720 % else:
908 721 <span class="block-right last-item noselect">Show hidden</span>
909 722 % endif
@@ -912,7 +725,7 b''
912 725
913 726 <div class="right-sidebar-expanded-state pr-details-content">
914 727 % if c.inline_comments_flat + c.comments:
915 ${comments_table(c.inline_comments_flat + c.comments, len(c.inline_comments_flat+c.comments))}
728 ${sidebar.comments_table(c.inline_comments_flat + c.comments, len(c.inline_comments_flat+c.comments))}
916 729 % else:
917 730 <table>
918 731 <tr>
@@ -942,7 +755,7 b''
942 755 <div class="right-sidebar-expanded-state pr-details-content">
943 756 <table>
944 757
945 <tr><td><code>${_('Pull Request Description')}</code></td></tr>
758 <tr><td><code>${_('In pull request description')}:</code></td></tr>
946 759 % if c.referenced_desc_issues:
947 760 % for ticket_dict in c.referenced_desc_issues:
948 761 <tr>
@@ -961,7 +774,7 b''
961 774 </tr>
962 775 % endif
963 776
964 <tr><td style="padding-top: 10px"><code>${_('Commit Messages')}</code></td></tr>
777 <tr><td style="padding-top: 10px"><code>${_('In commit messages')}:</code></td></tr>
965 778 % if c.referenced_commit_issues:
966 779 % for ticket_dict in c.referenced_commit_issues:
967 780 <tr>
@@ -1000,63 +813,12 b''
1000 813
1001 814 updateController = new UpdatePrController();
1002 815
1003 /** leak object to top level scope **/
1004 window.PullRequestPresenceController;
816 window.reviewerRulesData = ${c.pull_request_default_reviewers_data_json | n};
817 window.setReviewersData = ${c.pull_request_set_reviewers_data_json | n};
1005 818
1006 819 (function () {
1007 820 "use strict";
1008 821
1009 window.PullRequestPresenceController = function (channel) {
1010 var self = this;
1011 this.channel = channel;
1012 this.users = {};
1013
1014 this.storeUsers = function (users) {
1015 self.users = {}
1016 $.each(users, function(index, value) {
1017 var userId = value.state.id;
1018 self.users[userId] = value.state;
1019 })
1020 }
1021
1022 this.render = function () {
1023 $.each($('.reviewer_entry'), function(index, value) {
1024 var userData = $(value).data();
1025 if(self.users[userData.reviewerUserId] !== undefined){
1026 $(value).find('.presence-state').show();
1027 } else {
1028 $(value).find('.presence-state').hide();
1029 }
1030 })
1031 };
1032
1033 this.handlePresence = function (data) {
1034
1035 if (data.type == 'presence' && data.channel === self.channel) {
1036 this.storeUsers(data.users);
1037 this.render()
1038 }
1039 };
1040
1041 this.handleChannelUpdate = function (data) {
1042
1043 if (data.channel === this.channel) {
1044 this.storeUsers(data.state.users);
1045 this.render()
1046 }
1047
1048 };
1049
1050 /* subscribe our chat to topics that are interesting to it */
1051 $.Topic('/connection_controller/channel_update').subscribe(this.handleChannelUpdate.bind(this));
1052 $.Topic('/connection_controller/presence').subscribe(this.handlePresence.bind(this));
1053 };
1054
1055 })();
1056
1057
1058 $(function () {
1059
1060 822 // custom code mirror
1061 823 var codeMirrorInstance = $('#pr-description-input').get(0).MarkupForm.cm;
1062 824
@@ -1078,12 +840,13 b' window.PullRequestPresenceController;'
1078 840 },
1079 841
1080 842 edit: function (event) {
843 var cmInstance = $('#pr-description-input').get(0).MarkupForm.cm;
1081 844 this.viewFields.hide();
1082 845 this.editButton.hide();
1083 846 this.deleteButton.hide();
1084 847 this.closeButton.show();
1085 848 this.editFields.show();
1086 codeMirrorInstance.refresh();
849 cmInstance.refresh();
1087 850 },
1088 851
1089 852 view: function (event) {
@@ -1095,88 +858,24 b' window.PullRequestPresenceController;'
1095 858 }
1096 859 };
1097 860
1098 var ReviewersPanel = {
1099 editButton: $('#open_edit_reviewers'),
1100 closeButton: $('#close_edit_reviewers'),
1101 addButton: $('#add_reviewer'),
1102 removeButtons: $('.reviewer_member_remove,.reviewer_member_mandatory_remove'),
1103 reviewRules: ${c.pull_request_default_reviewers_data_json | n},
1104 setReviewers: ${c.pull_request_set_reviewers_data_json | n},
1105
1106 init: function () {
1107 var self = this;
1108 this.editButton.on('click', function (e) {
1109 self.edit();
1110 });
1111 this.closeButton.on('click', function (e) {
1112 self.close();
1113 self.renderReviewers();
1114 });
1115
1116 self.renderReviewers();
1117
1118 },
1119
1120 renderReviewers: function () {
1121
1122 $('#review_members').html('')
1123 $.each(this.setReviewers.reviewers, function (key, val) {
1124 var member = val;
861 PRDetails.init();
862 ReviewersPanel.init(reviewerRulesData, setReviewersData);
1125 863
1126 var entry = renderTemplate('reviewMemberEntry', {
1127 'member': member,
1128 'mandatory': member.mandatory,
1129 'reasons': member.reasons,
1130 'allowed_to_update': member.allowed_to_update,
1131 'review_status': member.review_status,
1132 'review_status_label': member.review_status_label,
1133 'user_group': member.user_group,
1134 'create': false
1135 });
1136
1137 $('#review_members').append(entry)
1138 });
1139 tooltipActivate();
1140
1141 },
1142
1143 edit: function (event) {
1144 this.editButton.hide();
1145 this.closeButton.show();
1146 this.addButton.show();
1147 $(this.removeButtons.selector).css('visibility', 'visible');
1148 // review rules
1149 reviewersController.loadReviewRules(this.reviewRules);
1150 },
1151
1152 close: function (event) {
1153 this.editButton.show();
1154 this.closeButton.hide();
1155 this.addButton.hide();
1156 $(this.removeButtons.selector).css('visibility', 'hidden');
1157 // hide review rules
1158 reviewersController.hideReviewRules()
1159 }
1160 };
1161
1162 PRDetails.init();
1163 ReviewersPanel.init();
1164
1165 showOutdated = function (self) {
864 window.showOutdated = function (self) {
1166 865 $('.comment-inline.comment-outdated').show();
1167 866 $('.filediff-outdated').show();
1168 867 $('.showOutdatedComments').hide();
1169 868 $('.hideOutdatedComments').show();
1170 869 };
1171 870
1172 hideOutdated = function (self) {
871 window.hideOutdated = function (self) {
1173 872 $('.comment-inline.comment-outdated').hide();
1174 873 $('.filediff-outdated').hide();
1175 874 $('.hideOutdatedComments').hide();
1176 875 $('.showOutdatedComments').show();
1177 876 };
1178 877
1179 refreshMergeChecks = function () {
878 window.refreshMergeChecks = function () {
1180 879 var loadUrl = "${request.current_route_path(_query=dict(merge_checks=1))}";
1181 880 $('.pull-request-merge').css('opacity', 0.3);
1182 881 $('.action-buttons-extra').css('opacity', 0.3);
@@ -1190,76 +889,7 b' window.PullRequestPresenceController;'
1190 889 );
1191 890 };
1192 891
1193 refreshComments = function () {
1194 var params = {
1195 'pull_request_id': templateContext.pull_request_data.pull_request_id,
1196 'repo_name': templateContext.repo_name,
1197 'version': '${request.GET.get('version', '')}',
1198 };
1199 var data = {"comments[]": ["1"]};
1200 var loadUrl = pyroutes.url('pullrequest_comments', params);
1201 var $targetElem = $('.comments-content-table');
1202 $targetElem.css('opacity', 0.3);
1203 $targetElem.load(
1204 loadUrl, data, function (responseText, textStatus, jqXHR) {
1205 if (jqXHR.status !== 200) {
1206 return false;
1207 }
1208 var $counterElem = $('#comments-count');
1209 var newCount = $(responseText).data('counter');
1210 if (newCount !== undefined) {
1211 var callback = function () {
1212 $counterElem.animate({'opacity': 1.00}, 200)
1213 $counterElem.html(newCount);
1214 };
1215 $counterElem.animate({'opacity': 0.15}, 200, callback);
1216 }
1217
1218
1219 $targetElem.css('opacity', 1);
1220 tooltipActivate();
1221 }
1222 );
1223 }
1224
1225 refreshTODOs = function () {
1226 var params = {
1227 'pull_request_id': templateContext.pull_request_data.pull_request_id,
1228 'repo_name': templateContext.repo_name,
1229 'version': '${request.GET.get('version', '')}',
1230 };
1231 var data = {"comments[]": ["1"]};
1232 var loadUrl = pyroutes.url('pullrequest_todos', params);
1233 var $targetElem = $('.todos-content-table');
1234 $targetElem.css('opacity', 0.3);
1235 $targetElem.load(
1236 loadUrl, data, function (responseText, textStatus, jqXHR) {
1237 if (jqXHR.status !== 200) {
1238 return false;
1239 }
1240 var $counterElem = $('#todos-count')
1241 var newCount = $(responseText).data('counter');
1242 if (newCount !== undefined) {
1243 var callback = function () {
1244 $counterElem.animate({'opacity': 1.00}, 200)
1245 $counterElem.html(newCount);
1246 };
1247 $counterElem.animate({'opacity': 0.15}, 200, callback);
1248 }
1249
1250 $targetElem.css('opacity', 1);
1251 tooltipActivate();
1252 }
1253 );
1254
1255 }
1256
1257 refreshAllComments = function() {
1258 refreshComments();
1259 refreshTODOs();
1260 }
1261
1262 closePullRequest = function (status) {
892 window.closePullRequest = function (status) {
1263 893 if (!confirm(_gettext('Are you sure to close this pull request without merging?'))) {
1264 894 return false;
1265 895 }
@@ -1269,6 +899,7 b' window.PullRequestPresenceController;'
1269 899 $(generalCommentForm.submitForm).submit();
1270 900 };
1271 901
902 //TODO this functionality is now missing
1272 903 $('#show-outdated-comments').on('click', function (e) {
1273 904 var button = $(this);
1274 905 var outdated = $('.comment-outdated');
@@ -1282,22 +913,6 b' window.PullRequestPresenceController;'
1282 913 }
1283 914 });
1284 915
1285 $('.show-inline-comments').on('change', function (e) {
1286 var show = 'none';
1287 var target = e.currentTarget;
1288 if (target.checked) {
1289 show = ''
1290 }
1291 var boxid = $(target).attr('id_for');
1292 var comments = $('#{0} .inline-comments'.format(boxid));
1293 var fn_display = function (idx) {
1294 $(this).css('display', show);
1295 };
1296 $(comments).each(fn_display);
1297 var btns = $('#{0} .inline-comments-button'.format(boxid));
1298 $(btns).each(fn_display);
1299 });
1300
1301 916 $('#merge_pull_request_form').submit(function () {
1302 917 if (!$('#merge_pull_request').attr('disabled')) {
1303 918 $('#merge_pull_request').attr('disabled', 'disabled');
@@ -1322,7 +937,6 b' window.PullRequestPresenceController;'
1322 937 "${c.repo_name}", "${c.pull_request.pull_request_id}");
1323 938 });
1324 939
1325
1326 940 // fixing issue with caches on firefox
1327 941 $('#update_commits').removeAttr("disabled");
1328 942
@@ -1343,6 +957,22 b' window.PullRequestPresenceController;'
1343 957 }
1344 958 });
1345 959
960 $('.show-inline-comments').on('change', function (e) {
961 var show = 'none';
962 var target = e.currentTarget;
963 if (target.checked) {
964 show = ''
965 }
966 var boxid = $(target).attr('id_for');
967 var comments = $('#{0} .inline-comments'.format(boxid));
968 var fn_display = function (idx) {
969 $(this).css('display', show);
970 };
971 $(comments).each(fn_display);
972 var btns = $('#{0} .inline-comments-button'.format(boxid));
973 $(btns).each(fn_display);
974 });
975
1346 976 // register submit callback on commentForm form to track TODOs
1347 977 window.commentFormGlobalSubmitSuccessCallback = function () {
1348 978 refreshMergeChecks();
@@ -1350,91 +980,12 b' window.PullRequestPresenceController;'
1350 980
1351 981 ReviewerAutoComplete('#user');
1352 982
1353 })
983 })();
1354 984
1355 985 $(document).ready(function () {
1356 986
1357 var $sideBar = $('.right-sidebar');
1358 var marginExpVal = '320'
1359 var marginColVal = '50'
1360 var marginExpanded = {'margin': '0 {0}px 0 0'.format(marginExpVal)};
1361 var marginCollapsed = {'margin': '0 {0}px 0 0'.format(marginColVal)};
1362 var marginExpandedHeader = {'margin': '0 -{0}px 0 0'.format(marginExpVal), 'z-index': 10000};
1363 var marginCollapsedHeader = {'margin': '0 -{0}px 0 0'.format(marginColVal), 'z-index': 10000};
1364
1365 var updateStickyHeader = function() {
1366 if (window.updateSticky !== undefined) {
1367 // potentially our comments change the active window size, so we
1368 // notify sticky elements
1369 updateSticky()
1370 }
1371 }
1372
1373 var expandSidebar = function() {
1374 var $sideBar = $('.right-sidebar');
1375 $('.outerwrapper').css(marginExpanded);
1376 $('.header').css(marginExpandedHeader);
1377 $('.sidebar-toggle a').html('<i class="icon-right" style="margin-right: -10px"></i><i class="icon-right"></i>');
1378 $('.right-sidebar-collapsed-state').hide();
1379 $('.right-sidebar-expanded-state').show();
1380
1381 $sideBar.addClass('right-sidebar-expanded')
1382 $sideBar.removeClass('right-sidebar-collapsed')
1383 }
1384
1385 var collapseSidebar = function() {
1386 var $sideBar = $('.right-sidebar');
1387 $('.outerwrapper').css(marginCollapsed);
1388 $('.header').css(marginCollapsedHeader);
1389 $('.sidebar-toggle a').html('<i class="icon-left" style="margin-right: -10px"></i><i class="icon-left"></i>');
1390 $('.right-sidebar-collapsed-state').show();
1391 $('.right-sidebar-expanded-state').hide();
1392
1393 $sideBar.removeClass('right-sidebar-expanded')
1394 $sideBar.addClass('right-sidebar-collapsed')
1395 }
1396
1397 toggleSidebar = function () {
1398 var $sideBar = $('.right-sidebar');
1399
1400 if ($sideBar.hasClass('right-sidebar-expanded')) {
1401 // expanded -> collapsed transition
1402 collapseSidebar();
1403 var sidebarState = 'collapsed';
1404
1405 } else {
1406 // collapsed -> expanded
1407 expandSidebar();
1408 var sidebarState = 'expanded';
1409 }
1410
1411 // update our other sticky header in same context
1412 updateStickyHeader();
1413 storeUserSessionAttr('rc_user_session_attr.sidebarState', sidebarState);
1414 }
1415
1416 var expanded = $sideBar.hasClass('right-sidebar-expanded');
1417
1418 if (templateContext.session_attrs.sidebarState === 'expanded') {
1419 expanded = true
1420 } else if (templateContext.session_attrs.sidebarState === 'collapsed') {
1421 expanded = false
1422 }
1423
1424 // show sidebar since it's hidden on load
1425 $('.right-sidebar').show();
1426
1427 // init based on set initial class, or if defined user session attrs
1428 if (expanded) {
1429 expandSidebar();
1430 updateStickyHeader();
1431
1432 } else {
1433 collapseSidebar();
1434 updateStickyHeader();
1435 }
1436 987 var channel = '${c.pr_broadcast_channel}';
1437 new PullRequestPresenceController(channel)
988 new ReviewerPresenceController(channel)
1438 989
1439 990 })
1440 991 </script>
General Comments 0
You need to be logged in to leave comments. Login now