##// END OF EJS Templates
sidebar: expose status indicator for general comments which changed review status.
marcink -
r4490:6bf75c53 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,203 +1,203 b''
1 1 # Copyright (C) 2016-2020 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 import logging
20 20
21 21 from rhodecode.translation import lazy_ugettext
22 22 from rhodecode.events.repo import (RepoEvent, _commits_as_dict, _issues_as_dict)
23 23
24 24 log = logging.getLogger(__name__)
25 25
26 26
27 27 class PullRequestEvent(RepoEvent):
28 28 """
29 29 Base class for pull request events.
30 30
31 31 :param pullrequest: a :class:`PullRequest` instance
32 32 """
33 33
34 34 def __init__(self, pullrequest):
35 35 super(PullRequestEvent, self).__init__(pullrequest.target_repo)
36 36 self.pullrequest = pullrequest
37 37
38 38 def as_dict(self):
39 39 from rhodecode.lib.utils2 import md5_safe
40 40 from rhodecode.model.pull_request import PullRequestModel
41 41 data = super(PullRequestEvent, self).as_dict()
42 42
43 43 commits = _commits_as_dict(
44 44 self,
45 45 commit_ids=self.pullrequest.revisions,
46 46 repos=[self.pullrequest.source_repo]
47 47 )
48 48 issues = _issues_as_dict(commits)
49 49 # calculate hashes of all commits for unique identifier of commits
50 50 # inside that pull request
51 51 commits_hash = md5_safe(':'.join(x.get('raw_id', '') for x in commits))
52 52
53 53 data.update({
54 54 'pullrequest': {
55 55 'title': self.pullrequest.title,
56 56 'issues': issues,
57 57 'pull_request_id': self.pullrequest.pull_request_id,
58 58 'url': PullRequestModel().get_url(
59 59 self.pullrequest, request=self.request),
60 60 'permalink_url': PullRequestModel().get_url(
61 61 self.pullrequest, request=self.request, permalink=True),
62 62 'shadow_url': PullRequestModel().get_shadow_clone_url(
63 63 self.pullrequest, request=self.request),
64 64 'status': self.pullrequest.calculated_review_status(),
65 65 'commits_uid': commits_hash,
66 66 'commits': commits,
67 67 }
68 68 })
69 69 return data
70 70
71 71
72 72 class PullRequestCreateEvent(PullRequestEvent):
73 73 """
74 74 An instance of this class is emitted as an :term:`event` after a pull
75 75 request is created.
76 76 """
77 77 name = 'pullrequest-create'
78 78 display_name = lazy_ugettext('pullrequest created')
79 79 description = lazy_ugettext('Event triggered after pull request was created')
80 80
81 81
82 82 class PullRequestCloseEvent(PullRequestEvent):
83 83 """
84 84 An instance of this class is emitted as an :term:`event` after a pull
85 85 request is closed.
86 86 """
87 87 name = 'pullrequest-close'
88 88 display_name = lazy_ugettext('pullrequest closed')
89 89 description = lazy_ugettext('Event triggered after pull request was closed')
90 90
91 91
92 92 class PullRequestUpdateEvent(PullRequestEvent):
93 93 """
94 94 An instance of this class is emitted as an :term:`event` after a pull
95 95 request's commits have been updated.
96 96 """
97 97 name = 'pullrequest-update'
98 98 display_name = lazy_ugettext('pullrequest commits updated')
99 99 description = lazy_ugettext('Event triggered after pull requests was updated')
100 100
101 101
102 102 class PullRequestReviewEvent(PullRequestEvent):
103 103 """
104 104 An instance of this class is emitted as an :term:`event` after a pull
105 105 request review has changed. A status defines new status of review.
106 106 """
107 107 name = 'pullrequest-review'
108 108 display_name = lazy_ugettext('pullrequest review changed')
109 109 description = lazy_ugettext('Event triggered after a review status of a '
110 110 'pull requests has changed to other.')
111 111
112 112 def __init__(self, pullrequest, status):
113 113 super(PullRequestReviewEvent, self).__init__(pullrequest)
114 114 self.status = status
115 115
116 116
117 117 class PullRequestMergeEvent(PullRequestEvent):
118 118 """
119 119 An instance of this class is emitted as an :term:`event` after a pull
120 120 request is merged.
121 121 """
122 122 name = 'pullrequest-merge'
123 123 display_name = lazy_ugettext('pullrequest merged')
124 124 description = lazy_ugettext('Event triggered after a successful merge operation '
125 125 'was executed on a pull request')
126 126
127 127
128 128 class PullRequestCommentEvent(PullRequestEvent):
129 129 """
130 130 An instance of this class is emitted as an :term:`event` after a pull
131 131 request comment is created.
132 132 """
133 133 name = 'pullrequest-comment'
134 134 display_name = lazy_ugettext('pullrequest commented')
135 135 description = lazy_ugettext('Event triggered after a comment was made on a code '
136 136 'in the pull request')
137 137
138 138 def __init__(self, pullrequest, comment):
139 139 super(PullRequestCommentEvent, self).__init__(pullrequest)
140 140 self.comment = comment
141 141
142 142 def as_dict(self):
143 143 from rhodecode.model.comment import CommentsModel
144 144 data = super(PullRequestCommentEvent, self).as_dict()
145 145
146 146 status = None
147 147 if self.comment.status_change:
148 status = self.comment.status_change[0].status
148 status = self.comment.review_status
149 149
150 150 data.update({
151 151 'comment': {
152 152 'status': status,
153 153 'text': self.comment.text,
154 154 'type': self.comment.comment_type,
155 155 'file': self.comment.f_path,
156 156 'line': self.comment.line_no,
157 157 'version': self.comment.last_version,
158 158 'url': CommentsModel().get_url(
159 159 self.comment, request=self.request),
160 160 'permalink_url': CommentsModel().get_url(
161 161 self.comment, request=self.request, permalink=True),
162 162 }
163 163 })
164 164 return data
165 165
166 166
167 167 class PullRequestCommentEditEvent(PullRequestEvent):
168 168 """
169 169 An instance of this class is emitted as an :term:`event` after a pull
170 170 request comment is edited.
171 171 """
172 172 name = 'pullrequest-comment-edit'
173 173 display_name = lazy_ugettext('pullrequest comment edited')
174 174 description = lazy_ugettext('Event triggered after a comment was edited on a code '
175 175 'in the pull request')
176 176
177 177 def __init__(self, pullrequest, comment):
178 178 super(PullRequestCommentEditEvent, self).__init__(pullrequest)
179 179 self.comment = comment
180 180
181 181 def as_dict(self):
182 182 from rhodecode.model.comment import CommentsModel
183 183 data = super(PullRequestCommentEditEvent, self).as_dict()
184 184
185 185 status = None
186 186 if self.comment.status_change:
187 status = self.comment.status_change[0].status
187 status = self.comment.review_status
188 188
189 189 data.update({
190 190 'comment': {
191 191 'status': status,
192 192 'text': self.comment.text,
193 193 'type': self.comment.comment_type,
194 194 'file': self.comment.f_path,
195 195 'line': self.comment.line_no,
196 196 'version': self.comment.last_version,
197 197 'url': CommentsModel().get_url(
198 198 self.comment, request=self.request),
199 199 'permalink_url': CommentsModel().get_url(
200 200 self.comment, request=self.request, permalink=True),
201 201 }
202 202 })
203 203 return data
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,140 +1,142 b''
1 1 ## snippet for sidebar elements
2 2 ## usage:
3 3 ## <%namespace name="sidebar" file="/base/sidebar.mako"/>
4 4 ## ${sidebar.comments_table()}
5 5 <%namespace name="base" file="/base/base.mako"/>
6 6
7 7 <%def name="comments_table(comments, counter_num, todo_comments=False, existing_ids=None, is_pr=True)">
8 8 <%
9 9 if todo_comments:
10 10 cls_ = 'todos-content-table'
11 11 def sorter(entry):
12 12 user_id = entry.author.user_id
13 13 resolved = '1' if entry.resolved else '0'
14 14 if user_id == c.rhodecode_user.user_id:
15 15 # own comments first
16 16 user_id = 0
17 17 return '{}'.format(str(entry.comment_id).zfill(10000))
18 18 else:
19 19 cls_ = 'comments-content-table'
20 20 def sorter(entry):
21 21 user_id = entry.author.user_id
22 22 return '{}'.format(str(entry.comment_id).zfill(10000))
23 23
24 24 existing_ids = existing_ids or []
25 25
26 26 %>
27 27
28 28 <table class="todo-table ${cls_}" data-total-count="${len(comments)}" data-counter="${counter_num}">
29 29
30 30 % for loop_obj, comment_obj in h.looper(reversed(sorted(comments, key=sorter))):
31 31 <%
32 32 display = ''
33 33 _cls = ''
34 34 %>
35 35
36 36 <%
37 37 comment_ver_index = comment_obj.get_index_version(getattr(c, 'versions', []))
38 38 prev_comment_ver_index = 0
39 39 if loop_obj.previous:
40 40 prev_comment_ver_index = loop_obj.previous.get_index_version(getattr(c, 'versions', []))
41 41
42 42 ver_info = None
43 43 if getattr(c, 'versions', []):
44 44 ver_info = c.versions[comment_ver_index-1] if comment_ver_index else None
45 45 %>
46 46 <% hidden_at_ver = comment_obj.outdated_at_version_js(c.at_version_num) %>
47 47 <% is_from_old_ver = comment_obj.older_than_version_js(c.at_version_num) %>
48 48 <%
49 49 if (prev_comment_ver_index > comment_ver_index):
50 50 comments_ver_divider = comment_ver_index
51 51 else:
52 52 comments_ver_divider = None
53 53 %>
54 54
55 55 % if todo_comments:
56 56 % if comment_obj.resolved:
57 57 <% _cls = 'resolved-todo' %>
58 58 <% display = 'none' %>
59 59 % endif
60 60 % else:
61 61 ## SKIP TODOs we display them in other area
62 62 % if comment_obj.is_todo:
63 63 <% display = 'none' %>
64 64 % endif
65 65 ## Skip outdated comments
66 66 % if comment_obj.outdated:
67 67 <% display = 'none' %>
68 68 <% _cls = 'hidden-comment' %>
69 69 % endif
70 70 % endif
71 71
72 72 % if not todo_comments and comments_ver_divider:
73 73 <tr class="old-comments-marker">
74 74 <td colspan="3">
75 75 % if ver_info:
76 76 <code>v${comments_ver_divider} ${h.age_component(ver_info.created_on, time_is_local=True, tooltip=False)}</code>
77 77 % else:
78 78 <code>v${comments_ver_divider}</code>
79 79 % endif
80 80 </td>
81 81 </tr>
82 82
83 83 % endif
84 84
85 85 <tr class="${_cls}" style="display: ${display};" data-sidebar-comment-id="${comment_obj.comment_id}">
86 86 <td class="td-todo-number">
87 87 <%
88 88 version_info = ''
89 89 if is_pr:
90 90 version_info = (' made in older version (v{})'.format(comment_ver_index) if is_from_old_ver == 'true' else ' made in this version')
91 91 %>
92
92 93 <script type="text/javascript">
93 94 // closure function helper
94 95 var sidebarComment${comment_obj.comment_id} = function() {
95 96 return renderTemplate('sideBarCommentHovercard', {
96 97 version_info: "${version_info}",
97 98 file_name: "${comment_obj.f_path}",
98 99 line_no: "${comment_obj.line_no}",
99 100 outdated: ${h.json.dumps(comment_obj.outdated)},
100 101 inline: ${h.json.dumps(comment_obj.is_inline)},
101 102 is_todo: ${h.json.dumps(comment_obj.is_todo)},
102 103 created_on: "${h.format_date(comment_obj.created_on)}",
103 104 datetime: "${comment_obj.created_on}${h.get_timezone(comment_obj.created_on, time_is_local=True)}",
105 review_status: "${(comment_obj.review_status or '')}"
104 106 })
105 107 }
106 108 </script>
107 109
108 110 % if comment_obj.outdated:
109 111 <i class="icon-comment-toggle tooltip-hovercard" data-hovercard-url="javascript:sidebarComment${comment_obj.comment_id}()"></i>
110 112 % elif comment_obj.is_inline:
111 113 <i class="icon-code tooltip-hovercard" data-hovercard-url="javascript:sidebarComment${comment_obj.comment_id}()"></i>
112 114 % else:
113 115 <i class="icon-comment tooltip-hovercard" data-hovercard-url="javascript:sidebarComment${comment_obj.comment_id}()"></i>
114 116 % endif
115 117
116 118 ## NEW, since refresh
117 119 % if existing_ids and comment_obj.comment_id not in existing_ids:
118 120 <span class="tag">NEW</span>
119 121 % endif
120 122 </td>
121 123
122 124 <td class="td-todo-gravatar">
123 125 ${base.gravatar(comment_obj.author.email, 16, user=comment_obj.author, tooltip=True, extra_class=['no-margin'])}
124 126 </td>
125 127 <td class="todo-comment-text-wrapper">
126 128 <div class="todo-comment-text ${('todo-resolved' if comment_obj.resolved else '')}">
127 129 <a class="${('todo-resolved' if comment_obj.resolved else '')} permalink"
128 130 href="#comment-${comment_obj.comment_id}"
129 131 onclick="return Rhodecode.comments.scrollToComment($('#comment-${comment_obj.comment_id}'), 0, ${hidden_at_ver})">
130 132
131 133 ${h.chop_at_smart(comment_obj.text, '\n', suffix_if_chopped='...')}
132 134 </a>
133 135 </div>
134 136 </td>
135 137 </tr>
136 138 % endfor
137 139
138 140 </table>
139 141
140 142 </%def> No newline at end of file
@@ -1,518 +1,518 b''
1 1 ## -*- coding: utf-8 -*-
2 2 ## usage:
3 3 ## <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
4 4 ## ${comment.comment_block(comment)}
5 5 ##
6 6
7 7 <%!
8 8 from rhodecode.lib import html_filters
9 9 %>
10 10
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 14 <%
15 15 from rhodecode.model.comment import CommentsModel
16 16 comment_model = CommentsModel()
17 17 %>
18 18 <% comment_ver = comment.get_index_version(getattr(c, 'versions', [])) %>
19 19 <% latest_ver = len(getattr(c, 'versions', [])) %>
20 20
21 21 % if inline:
22 22 <% outdated_at_ver = comment.outdated_at_version(c.at_version_num) %>
23 23 % else:
24 24 <% outdated_at_ver = comment.older_than_version(c.at_version_num) %>
25 25 % endif
26 26
27 27 <div class="comment
28 28 ${'comment-inline' if inline else 'comment-general'}
29 29 ${'comment-outdated' if outdated_at_ver else 'comment-current'}"
30 30 id="comment-${comment.comment_id}"
31 31 line="${comment.line_no}"
32 32 data-comment-id="${comment.comment_id}"
33 33 data-comment-type="${comment.comment_type}"
34 34 data-comment-renderer="${comment.renderer}"
35 35 data-comment-text="${comment.text | html_filters.base64,n}"
36 36 data-comment-line-no="${comment.line_no}"
37 37 data-comment-inline=${h.json.dumps(inline)}
38 38 style="${'display: none;' if outdated_at_ver else ''}">
39 39
40 40 <div class="meta">
41 41 <div class="comment-type-label">
42 42 <div class="comment-label ${comment.comment_type or 'note'}" id="comment-label-${comment.comment_id}">
43 43
44 44 ## TODO COMMENT
45 45 % if comment.comment_type == 'todo':
46 46 % if comment.resolved:
47 47 <div class="resolved tooltip" title="${_('Resolved by comment #{}').format(comment.resolved.comment_id)}">
48 48 <i class="icon-flag-filled"></i>
49 49 <a href="#comment-${comment.resolved.comment_id}">${comment.comment_type}</a>
50 50 </div>
51 51 % else:
52 52 <div class="resolved tooltip" style="display: none">
53 53 <span>${comment.comment_type}</span>
54 54 </div>
55 55 <div class="resolve tooltip" onclick="return Rhodecode.comments.createResolutionComment(${comment.comment_id});" title="${_('Click to create resolution comment.')}">
56 56 <i class="icon-flag-filled"></i>
57 57 ${comment.comment_type}
58 58 </div>
59 59 % endif
60 60 ## NOTE COMMENT
61 61 % else:
62 62 ## RESOLVED NOTE
63 63 % if comment.resolved_comment:
64 64 <div class="tooltip" title="${_('This comment resolves TODO #{}').format(comment.resolved_comment.comment_id)}">
65 65 fix
66 66 <a href="#comment-${comment.resolved_comment.comment_id}" onclick="Rhodecode.comments.scrollToComment($('#comment-${comment.resolved_comment.comment_id}'), 0, ${h.json.dumps(comment.resolved_comment.outdated)})">
67 67 <span style="text-decoration: line-through">#${comment.resolved_comment.comment_id}</span>
68 68 </a>
69 69 </div>
70 70 ## STATUS CHANGE NOTE
71 71 % elif not comment.is_inline and comment.status_change:
72 72 <%
73 73 if comment.pull_request:
74 74 status_change_title = 'Status of review for pull request !{}'.format(comment.pull_request.pull_request_id)
75 75 else:
76 76 status_change_title = 'Status of review for commit {}'.format(h.short_id(comment.commit_id))
77 77 %>
78 78
79 <i class="icon-circle review-status-${comment.status_change[0].status}"></i>
79 <i class="icon-circle review-status-${comment.review_status}"></i>
80 80 <div class="changeset-status-lbl tooltip" title="${status_change_title}">
81 ${comment.status_change[0].status_lbl}
81 ${comment.review_status_lbl}
82 82 </div>
83 83 % else:
84 84 <div>
85 85 <i class="icon-comment"></i>
86 86 ${(comment.comment_type or 'note')}
87 87 </div>
88 88 % endif
89 89 % endif
90 90
91 91 </div>
92 92 </div>
93 93
94 94 % if 0 and comment.status_change:
95 95 <div class="pull-left">
96 96 <span class="tag authortag tooltip" title="${_('Status from pull request.')}">
97 97 <a href="${h.route_path('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id)}">
98 98 ${'!{}'.format(comment.pull_request.pull_request_id)}
99 99 </a>
100 100 </span>
101 101 </div>
102 102 % endif
103 103
104 104 <div class="author ${'author-inline' if inline else 'author-general'}">
105 105 ${base.gravatar_with_user(comment.author.email, 16, tooltip=True)}
106 106 </div>
107 107
108 108 <div class="date">
109 109 ${h.age_component(comment.modified_at, time_is_local=True)}
110 110 </div>
111 111
112 112 % if comment.pull_request and comment.pull_request.author.user_id == comment.author.user_id:
113 113 <span class="tag authortag tooltip" title="${_('Pull request author')}">
114 114 ${_('author')}
115 115 </span>
116 116 % endif
117 117
118 118 <%
119 119 comment_version_selector = 'comment_versions_{}'.format(comment.comment_id)
120 120 %>
121 121
122 122 % if comment.history:
123 123 <div class="date">
124 124
125 125 <input id="${comment_version_selector}" name="${comment_version_selector}"
126 126 type="hidden"
127 127 data-last-version="${comment.history[-1].version}">
128 128
129 129 <script type="text/javascript">
130 130
131 131 var preLoadVersionData = [
132 132 % for comment_history in comment.history:
133 133 {
134 134 id: ${comment_history.comment_history_id},
135 135 text: 'v${comment_history.version}',
136 136 action: function () {
137 137 Rhodecode.comments.showVersion(
138 138 "${comment.comment_id}",
139 139 "${comment_history.comment_history_id}"
140 140 )
141 141 },
142 142 comment_version: "${comment_history.version}",
143 143 comment_author_username: "${comment_history.author.username}",
144 144 comment_author_gravatar: "${h.gravatar_url(comment_history.author.email, 16)}",
145 145 comment_created_on: '${h.age_component(comment_history.created_on, time_is_local=True)}',
146 146 },
147 147 % endfor
148 148 ]
149 149 initVersionSelector("#${comment_version_selector}", {results: preLoadVersionData});
150 150
151 151 </script>
152 152
153 153 </div>
154 154 % else:
155 155 <div class="date" style="display: none">
156 156 <input id="${comment_version_selector}" name="${comment_version_selector}"
157 157 type="hidden"
158 158 data-last-version="0">
159 159 </div>
160 160 %endif
161 161
162 162 <div class="comment-links-block">
163 163
164 164 % if inline:
165 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))}">
166 166 % if outdated_at_ver:
167 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 168 <code class="action-divider">|</code>
169 169 % elif comment_ver:
170 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 171 <code class="action-divider">|</code>
172 172 % endif
173 173 </a>
174 174 % else:
175 175 % if comment_ver:
176 176
177 177 % if comment.outdated:
178 178 <a class="pr-version"
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 182 </a>
183 183 <code class="action-divider">|</code>
184 184 % else:
185 185 <a class="tooltip pr-version"
186 186 title="${_('Comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}"
187 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)}"
188 188 >
189 189 <code class="pr-version-num">${'v{}'.format(comment_ver)}</code>
190 190 </a>
191 191 <code class="action-divider">|</code>
192 192 % endif
193 193
194 194 % endif
195 195 % endif
196 196
197 197 <details class="details-reset details-inline-block">
198 198 <summary class="noselect"><i class="icon-options cursor-pointer"></i></summary>
199 199 <details-menu class="details-dropdown">
200 200
201 201 <div class="dropdown-item">
202 202 ${_('Comment')} #${comment.comment_id}
203 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 204 </div>
205 205
206 206 ## show delete comment if it's not a PR (regular comments) or it's PR that is not closed
207 207 ## only super-admin, repo admin OR comment owner can delete, also hide delete if currently viewed comment is outdated
208 208 %if not outdated_at_ver and (not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed())):
209 209 ## permissions to delete
210 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):
211 211 <div class="dropdown-divider"></div>
212 212 <div class="dropdown-item">
213 213 <a onclick="return Rhodecode.comments.editComment(this);" class="btn btn-link btn-sm edit-comment">${_('Edit')}</a>
214 214 </div>
215 215 <div class="dropdown-item">
216 216 <a onclick="return Rhodecode.comments.deleteComment(this);" class="btn btn-link btn-sm btn-danger delete-comment">${_('Delete')}</a>
217 217 </div>
218 218 %else:
219 219 <div class="dropdown-divider"></div>
220 220 <div class="dropdown-item">
221 221 <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Edit')}</a>
222 222 </div>
223 223 <div class="dropdown-item">
224 224 <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Delete')}</a>
225 225 </div>
226 226 %endif
227 227 %else:
228 228 <div class="dropdown-divider"></div>
229 229 <div class="dropdown-item">
230 230 <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Edit')}</a>
231 231 </div>
232 232 <div class="dropdown-item">
233 233 <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Delete')}</a>
234 234 </div>
235 235 %endif
236 236 </details-menu>
237 237 </details>
238 238
239 239 <code class="action-divider">|</code>
240 240 % if outdated_at_ver:
241 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 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>
243 243 % else:
244 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 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>
246 246 % endif
247 247
248 248 </div>
249 249 </div>
250 250 <div class="text">
251 251 ${h.render(comment.text, renderer=comment.renderer, mentions=True, repo_name=getattr(c, 'repo_name', None), active_pattern_entries=active_pattern_entries)}
252 252 </div>
253 253
254 254 </div>
255 255 </%def>
256 256
257 257 ## generate main comments
258 258 <%def name="generate_comments(comments, include_pull_request=False, is_pull_request=False)">
259 259 <%
260 260 active_pattern_entries = h.get_active_pattern_entries(getattr(c, 'repo_name', None))
261 261 %>
262 262
263 263 <div class="general-comments" id="comments">
264 264 %for comment in comments:
265 265 <div id="comment-tr-${comment.comment_id}">
266 266 ## only render comments that are not from pull request, or from
267 267 ## pull request and a status change
268 268 %if not comment.pull_request or (comment.pull_request and comment.status_change) or include_pull_request:
269 269 ${comment_block(comment, active_pattern_entries=active_pattern_entries)}
270 270 %endif
271 271 </div>
272 272 %endfor
273 273 ## to anchor ajax comments
274 274 <div id="injected_page_comments"></div>
275 275 </div>
276 276 </%def>
277 277
278 278
279 279 <%def name="comments(post_url, cur_status, is_pull_request=False, is_compare=False, change_status=True, form_extras=None)">
280 280
281 281 <div class="comments">
282 282 <%
283 283 if is_pull_request:
284 284 placeholder = _('Leave a comment on this Pull Request.')
285 285 elif is_compare:
286 286 placeholder = _('Leave a comment on {} commits in this range.').format(len(form_extras))
287 287 else:
288 288 placeholder = _('Leave a comment on this Commit.')
289 289 %>
290 290
291 291 % if c.rhodecode_user.username != h.DEFAULT_USER:
292 292 <div class="js-template" id="cb-comment-general-form-template">
293 293 ## template generated for injection
294 294 ${comment_form(form_type='general', review_statuses=c.commit_statuses, form_extras=form_extras)}
295 295 </div>
296 296
297 297 <div id="cb-comment-general-form-placeholder" class="comment-form ac">
298 298 ## inject form here
299 299 </div>
300 300 <script type="text/javascript">
301 301 var lineNo = 'general';
302 302 var resolvesCommentId = null;
303 303 var generalCommentForm = Rhodecode.comments.createGeneralComment(
304 304 lineNo, "${placeholder}", resolvesCommentId);
305 305
306 306 // set custom success callback on rangeCommit
307 307 % if is_compare:
308 308 generalCommentForm.setHandleFormSubmit(function(o) {
309 309 var self = generalCommentForm;
310 310
311 311 var text = self.cm.getValue();
312 312 var status = self.getCommentStatus();
313 313 var commentType = self.getCommentType();
314 314
315 315 if (text === "" && !status) {
316 316 return;
317 317 }
318 318
319 319 // we can pick which commits we want to make the comment by
320 320 // selecting them via click on preview pane, this will alter the hidden inputs
321 321 var cherryPicked = $('#changeset_compare_view_content .compare_select.hl').length > 0;
322 322
323 323 var commitIds = [];
324 324 $('#changeset_compare_view_content .compare_select').each(function(el) {
325 325 var commitId = this.id.replace('row-', '');
326 326 if ($(this).hasClass('hl') || !cherryPicked) {
327 327 $("input[data-commit-id='{0}']".format(commitId)).val(commitId);
328 328 commitIds.push(commitId);
329 329 } else {
330 330 $("input[data-commit-id='{0}']".format(commitId)).val('')
331 331 }
332 332 });
333 333
334 334 self.setActionButtonsDisabled(true);
335 335 self.cm.setOption("readOnly", true);
336 336 var postData = {
337 337 'text': text,
338 338 'changeset_status': status,
339 339 'comment_type': commentType,
340 340 'commit_ids': commitIds,
341 341 'csrf_token': CSRF_TOKEN
342 342 };
343 343
344 344 var submitSuccessCallback = function(o) {
345 345 location.reload(true);
346 346 };
347 347 var submitFailCallback = function(){
348 348 self.resetCommentFormState(text)
349 349 };
350 350 self.submitAjaxPOST(
351 351 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
352 352 });
353 353 % endif
354 354
355 355 </script>
356 356 % else:
357 357 ## form state when not logged in
358 358 <div class="comment-form ac">
359 359
360 360 <div class="comment-area">
361 361 <div class="comment-area-header">
362 362 <ul class="nav-links clearfix">
363 363 <li class="active">
364 364 <a class="disabled" href="#edit-btn" disabled="disabled" onclick="return false">${_('Write')}</a>
365 365 </li>
366 366 <li class="">
367 367 <a class="disabled" href="#preview-btn" disabled="disabled" onclick="return false">${_('Preview')}</a>
368 368 </li>
369 369 </ul>
370 370 </div>
371 371
372 372 <div class="comment-area-write" style="display: block;">
373 373 <div id="edit-container">
374 374 <div style="padding: 40px 0">
375 375 ${_('You need to be logged in to leave comments.')}
376 376 <a href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">${_('Login now')}</a>
377 377 </div>
378 378 </div>
379 379 <div id="preview-container" class="clearfix" style="display: none;">
380 380 <div id="preview-box" class="preview-box"></div>
381 381 </div>
382 382 </div>
383 383
384 384 <div class="comment-area-footer">
385 385 <div class="toolbar">
386 386 <div class="toolbar-text">
387 387 </div>
388 388 </div>
389 389 </div>
390 390 </div>
391 391
392 392 <div class="comment-footer">
393 393 </div>
394 394
395 395 </div>
396 396 % endif
397 397
398 398 <script type="text/javascript">
399 399 bindToggleButtons();
400 400 </script>
401 401 </div>
402 402 </%def>
403 403
404 404
405 405 <%def name="comment_form(form_type, form_id='', lineno_id='{1}', review_statuses=None, form_extras=None)">
406 406
407 407 ## comment injected based on assumption that user is logged in
408 408 <form ${('id="{}"'.format(form_id) if form_id else '') |n} action="#" method="GET">
409 409
410 410 <div class="comment-area">
411 411 <div class="comment-area-header">
412 412 <div class="pull-left">
413 413 <ul class="nav-links clearfix">
414 414 <li class="active">
415 415 <a href="#edit-btn" tabindex="-1" id="edit-btn_${lineno_id}">${_('Write')}</a>
416 416 </li>
417 417 <li class="">
418 418 <a href="#preview-btn" tabindex="-1" id="preview-btn_${lineno_id}">${_('Preview')}</a>
419 419 </li>
420 420 </ul>
421 421 </div>
422 422 <div class="pull-right">
423 423 <span class="comment-area-text">${_('Mark as')}:</span>
424 424 <select class="comment-type" id="comment_type_${lineno_id}" name="comment_type">
425 425 % for val in c.visual.comment_types:
426 426 <option value="${val}">${val.upper()}</option>
427 427 % endfor
428 428 </select>
429 429 </div>
430 430 </div>
431 431
432 432 <div class="comment-area-write" style="display: block;">
433 433 <div id="edit-container_${lineno_id}">
434 434 <textarea id="text_${lineno_id}" name="text" class="comment-block-ta ac-input"></textarea>
435 435 </div>
436 436 <div id="preview-container_${lineno_id}" class="clearfix" style="display: none;">
437 437 <div id="preview-box_${lineno_id}" class="preview-box"></div>
438 438 </div>
439 439 </div>
440 440
441 441 <div class="comment-area-footer comment-attachment-uploader">
442 442 <div class="toolbar">
443 443
444 444 <div class="comment-attachment-text">
445 445 <div class="dropzone-text">
446 446 ${_("Drag'n Drop files here or")} <span class="link pick-attachment">${_('Choose your files')}</span>.<br>
447 447 </div>
448 448 <div class="dropzone-upload" style="display:none">
449 449 <i class="icon-spin animate-spin"></i> ${_('uploading...')}
450 450 </div>
451 451 </div>
452 452
453 453 ## comments dropzone template, empty on purpose
454 454 <div style="display: none" class="comment-attachment-uploader-template">
455 455 <div class="dz-file-preview" style="margin: 0">
456 456 <div class="dz-error-message"></div>
457 457 </div>
458 458 </div>
459 459
460 460 </div>
461 461 </div>
462 462 </div>
463 463
464 464 <div class="comment-footer">
465 465
466 466 ## inject extra inputs into the form
467 467 % if form_extras and isinstance(form_extras, (list, tuple)):
468 468 <div id="comment_form_extras">
469 469 % for form_ex_el in form_extras:
470 470 ${form_ex_el|n}
471 471 % endfor
472 472 </div>
473 473 % endif
474 474
475 475 <div class="action-buttons">
476 476 % if form_type != 'inline':
477 477 <div class="action-buttons-extra"></div>
478 478 % endif
479 479
480 480 <input class="btn btn-success comment-button-input" id="save_${lineno_id}" name="save" type="submit" value="${_('Comment')}">
481 481
482 482 ## inline for has a file, and line-number together with cancel hide button.
483 483 % if form_type == 'inline':
484 484 <input type="hidden" name="f_path" value="{0}">
485 485 <input type="hidden" name="line" value="${lineno_id}">
486 486 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
487 487 ${_('Cancel')}
488 488 </button>
489 489 % endif
490 490 </div>
491 491
492 492 % if review_statuses:
493 493 <div class="status_box">
494 494 <select id="change_status_${lineno_id}" name="changeset_status">
495 495 <option></option> ## Placeholder
496 496 % for status, lbl in review_statuses:
497 497 <option value="${status}" data-status="${status}">${lbl}</option>
498 498 %if is_pull_request and change_status and status in ('approved', 'rejected'):
499 499 <option value="${status}_closed" data-status="${status}">${lbl} & ${_('Closed')}</option>
500 500 %endif
501 501 % endfor
502 502 </select>
503 503 </div>
504 504 % endif
505 505
506 506 <div class="toolbar-text">
507 507 <% renderer_url = '<a href="%s">%s</a>' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper()) %>
508 508 ${_('Comments parsed using {} syntax.').format(renderer_url)|n} <br/>
509 509 <span class="tooltip" title="${_('Use @username inside this text to send notification to this RhodeCode user')}">@mention</span>
510 510 ${_('and')}
511 511 <span class="tooltip" title="${_('Start typing with / for certain actions to be triggered via text box.')}">`/` autocomplete</span>
512 512 ${_('actions supported.')}
513 513 </div>
514 514 </div>
515 515
516 516 </form>
517 517
518 518 </%def> No newline at end of file
@@ -1,236 +1,242 b''
1 1 <%text>
2 2 <div style="display: none">
3 3
4 4 <script>
5 5 var CG = new ColorGenerator();
6 6 </script>
7 7
8 8 <script id="ejs_gravatarWithUser" type="text/template" class="ejsTemplate">
9 9
10 10 <%
11 11 if (size > 16) {
12 12 var gravatar_class = 'gravatar gravatar-large';
13 13 } else {
14 14 var gravatar_class = 'gravatar';
15 15 }
16 16
17 17 if (tooltip) {
18 18 var gravatar_class = gravatar_class + ' tooltip-hovercard';
19 19 }
20 20
21 21 var data_hovercard_alt = username;
22 22
23 23 %>
24 24
25 25 <%
26 26 if (show_disabled) {
27 27 var user_cls = 'user user-disabled';
28 28 } else {
29 29 var user_cls = 'user';
30 30 }
31 31 var data_hovercard_url = pyroutes.url('hovercard_user', {"user_id": user_id})
32 32 %>
33 33
34 34 <div class="rc-user">
35 35 <img class="<%= gravatar_class %>" height="<%= size %>" width="<%= size %>" data-hovercard-url="<%= data_hovercard_url %>" data-hovercard-alt="<%= data_hovercard_alt %>" src="<%- gravatar_url -%>">
36 36 <span class="<%= user_cls %>"> <%- user_link -%> </span>
37 37 </div>
38 38
39 39 </script>
40 40
41 41 <script id="ejs_reviewMemberEntry" type="text/template" class="ejsTemplate">
42 42 <%
43 43 if (create) {
44 44 var edit_visibility = 'visible';
45 45 } else {
46 46 var edit_visibility = 'hidden';
47 47 }
48 48
49 49 if (member.user_group && member.user_group.vote_rule) {
50 50 var reviewGroup = '<i class="icon-user-group"></i>';
51 51 var reviewGroupColor = CG.asRGB(CG.getColor(member.user_group.vote_rule));
52 52 } else {
53 53 var reviewGroup = null;
54 54 var reviewGroupColor = 'transparent';
55 55 }
56 56 var rule_show = rule_show || false;
57 57
58 58 if (rule_show) {
59 59 var rule_visibility = 'table-cell';
60 60 } else {
61 61 var rule_visibility = 'none';
62 62 }
63 63
64 64 %>
65 65
66 66 <tr id="reviewer_<%= member.user_id %>" class="reviewer_entry" tooltip="Review Group" data-reviewer-user-id="<%= member.user_id %>">
67 67
68 68 <td style="width: 20px">
69 69 <div class="reviewer_status tooltip" title="<%= review_status_label %>">
70 70 <i class="icon-circle review-status-<%= review_status %>"></i>
71 71 </div>
72 72 </td>
73 73
74 74 <td>
75 75 <div id="reviewer_<%= member.user_id %>_name" class="reviewer_name">
76 76 <%-
77 77 renderTemplate('gravatarWithUser', {
78 78 'size': 16,
79 79 'show_disabled': false,
80 80 'tooltip': true,
81 81 'username': member.username,
82 82 'user_id': member.user_id,
83 83 'user_link': member.user_link,
84 84 'gravatar_url': member.gravatar_link
85 85 })
86 86 %>
87 87 <span class="tooltip presence-state" style="display: none" title="This users is currently at this page">
88 88 <i class="icon-eye" style="color: #0ac878"></i>
89 89 </span>
90 90 </div>
91 91 </td>
92 92
93 93 <td style="width: 10px">
94 94 <% if (reviewGroup !== null) { %>
95 95 <span class="tooltip" title="Member of review group from rule: `<%= member.user_group.name %>`" style="color: <%= reviewGroupColor %>">
96 96 <%- reviewGroup %>
97 97 </span>
98 98 <% } %>
99 99 </td>
100 100
101 101 <% if (mandatory) { %>
102 102 <td style="text-align: right;width: 10px;">
103 103 <div class="reviewer_member_mandatory tooltip" title="Mandatory reviewer">
104 104 <i class="icon-lock"></i>
105 105 </div>
106 106 </td>
107 107
108 108 <% } else { %>
109 109 <td style="text-align: right;width: 10px;">
110 110 <% if (allowed_to_update) { %>
111 111 <div class="reviewer_member_remove" onclick="reviewersController.removeReviewMember(<%= member.user_id %>, true)" style="visibility: <%= edit_visibility %>;">
112 112 <i class="icon-remove"></i>
113 113 </div>
114 114 <% } %>
115 115 </td>
116 116 <% } %>
117 117
118 118 </tr>
119 119
120 120 <tr>
121 121 <td colspan="4" style="display: <%= rule_visibility %>" class="pr-user-rule-container">
122 122 <input type="hidden" name="__start__" value="reviewer:mapping">
123 123
124 124 <%if (member.user_group && member.user_group.vote_rule) { %>
125 125 <div class="reviewer_reason">
126 126
127 127 <%if (member.user_group.vote_rule == -1) {%>
128 128 - group votes required: ALL
129 129 <%} else {%>
130 130 - group votes required: <%= member.user_group.vote_rule %>
131 131 <%}%>
132 132 </div>
133 133 <%} %>
134 134
135 135 <input type="hidden" name="__start__" value="reasons:sequence">
136 136 <% for (var i = 0; i < reasons.length; i++) { %>
137 137 <% var reason = reasons[i] %>
138 138 <div class="reviewer_reason">- <%= reason %></div>
139 139 <input type="hidden" name="reason" value="<%= reason %>">
140 140 <% } %>
141 141 <input type="hidden" name="__end__" value="reasons:sequence">
142 142
143 143 <input type="hidden" name="__start__" value="rules:sequence">
144 144 <% for (var i = 0; i < member.rules.length; i++) { %>
145 145 <% var rule = member.rules[i] %>
146 146 <input type="hidden" name="rule_id" value="<%= rule %>">
147 147 <% } %>
148 148 <input type="hidden" name="__end__" value="rules:sequence">
149 149
150 150 <input id="reviewer_<%= member.user_id %>_input" type="hidden" value="<%= member.user_id %>" name="user_id" />
151 151 <input type="hidden" name="mandatory" value="<%= mandatory %>"/>
152 152
153 153 <input type="hidden" name="__end__" value="reviewer:mapping">
154 154 </td>
155 155 </tr>
156 156
157 157 </script>
158 158
159 159 <script id="ejs_commentVersion" type="text/template" class="ejsTemplate">
160 160
161 161 <%
162 162 if (size > 16) {
163 163 var gravatar_class = 'gravatar gravatar-large';
164 164 } else {
165 165 var gravatar_class = 'gravatar';
166 166 }
167 167
168 168 %>
169 169
170 170 <%
171 171 if (show_disabled) {
172 172 var user_cls = 'user user-disabled';
173 173 } else {
174 174 var user_cls = 'user';
175 175 }
176 176
177 177 %>
178 178
179 179 <div style='line-height: 20px'>
180 180 <img style="margin: -3px 0" class="<%= gravatar_class %>" height="<%= size %>" width="<%= size %>" src="<%- gravatar_url -%>">
181 181 <strong><%- user_name -%></strong>, <code>v<%- version -%></code> edited <%- timeago_component -%>
182 182 </div>
183 183
184 184 </script>
185 185
186 186
187 187 <script id="ejs_sideBarCommentHovercard" type="text/template" class="ejsTemplate">
188 188
189 189 <div>
190 190 <% if (is_todo) { %>
191 191 <% if (inline) { %>
192 192 <strong>Inline</strong> TODO on line: <%= line_no %>
193 193 <% if (version_info) { %>
194 194 <%= version_info %>
195 195 <% } %>
196 196 <br/>
197 197 File: <code><%- file_name -%></code>
198 198 <% } else { %>
199 <% if (review_status) { %>
200 <i class="icon-circle review-status-<%= review_status %>"></i>
201 <% } %>
199 202 <strong>General</strong> TODO
200 203 <% if (version_info) { %>
201 204 <%= version_info %>
202 205 <% } %>
203 206 <% } %>
204 207 <% } else { %>
205 208 <% if (inline) { %>
206 209 <strong>Inline</strong> comment on line: <%= line_no %>
207 210 <% if (version_info) { %>
208 211 <%= version_info %>
209 212 <% } %>
210 213 <br/>
211 214 File: <code><%- file_name -%></code>
212 215 <% } else { %>
216 <% if (review_status) { %>
217 <i class="icon-circle review-status-<%= review_status %>"></i>
218 <% } %>
213 219 <strong>General</strong> comment
214 220 <% if (version_info) { %>
215 221 <%= version_info %>
216 222 <% } %>
217 223 <% } %>
218 224 <% } %>
219 225 <br/>
220 226 Created:
221 227 <time class="timeago" title="<%= created_on %>" datetime="<%= datetime %>"><%= $.timeago(datetime) %></time>
222 228
223 229 </div>
224 230
225 231 </script>
226 232
227 233 ##// END OF EJS Templates
228 234 </div>
229 235
230 236
231 237 <script>
232 238 // registers the templates into global cache
233 239 registerTemplates();
234 240 </script>
235 241
236 242 </%text>
General Comments 0
You need to be logged in to leave comments. Login now