Show More
@@ -0,0 +1,53 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | ||||
|
3 | import logging | |||
|
4 | from sqlalchemy import * | |||
|
5 | ||||
|
6 | from alembic.migration import MigrationContext | |||
|
7 | from alembic.operations import Operations | |||
|
8 | ||||
|
9 | from rhodecode.lib.dbmigrate.versions import _reset_base | |||
|
10 | from rhodecode.model import meta, init_model_encryption | |||
|
11 | ||||
|
12 | ||||
|
13 | log = logging.getLogger(__name__) | |||
|
14 | ||||
|
15 | ||||
|
16 | def upgrade(migrate_engine): | |||
|
17 | """ | |||
|
18 | Upgrade operations go here. | |||
|
19 | Don't create your own engine; bind migrate_engine to your metadata | |||
|
20 | """ | |||
|
21 | _reset_base(migrate_engine) | |||
|
22 | from rhodecode.lib.dbmigrate.schema import db_4_20_0_0 as db | |||
|
23 | ||||
|
24 | init_model_encryption(db) | |||
|
25 | ||||
|
26 | context = MigrationContext.configure(migrate_engine.connect()) | |||
|
27 | op = Operations(context) | |||
|
28 | ||||
|
29 | table = db.ChangesetComment.__table__ | |||
|
30 | with op.batch_alter_table(table.name) as batch_op: | |||
|
31 | new_column = Column('draft', Boolean(), nullable=True) | |||
|
32 | batch_op.add_column(new_column) | |||
|
33 | ||||
|
34 | _set_default_as_non_draft(op, meta.Session) | |||
|
35 | ||||
|
36 | ||||
|
37 | def downgrade(migrate_engine): | |||
|
38 | meta = MetaData() | |||
|
39 | meta.bind = migrate_engine | |||
|
40 | ||||
|
41 | ||||
|
42 | def fixups(models, _SESSION): | |||
|
43 | pass | |||
|
44 | ||||
|
45 | ||||
|
46 | def _set_default_as_non_draft(op, session): | |||
|
47 | params = {'draft': False} | |||
|
48 | query = text( | |||
|
49 | 'UPDATE changeset_comments SET draft = :draft' | |||
|
50 | ).bindparams(**params) | |||
|
51 | op.execute(query) | |||
|
52 | session().commit() | |||
|
53 |
@@ -48,7 +48,7 b' PYRAMID_SETTINGS = {}' | |||||
48 | EXTENSIONS = {} |
|
48 | EXTENSIONS = {} | |
49 |
|
49 | |||
50 | __version__ = ('.'.join((str(each) for each in VERSION[:3]))) |
|
50 | __version__ = ('.'.join((str(each) for each in VERSION[:3]))) | |
51 |
__dbversion__ = 11 |
|
51 | __dbversion__ = 111 # defines current db version for migrations | |
52 | __platform__ = platform.system() |
|
52 | __platform__ = platform.system() | |
53 | __license__ = 'AGPLv3, and Commercial License' |
|
53 | __license__ = 'AGPLv3, and Commercial License' | |
54 | __author__ = 'RhodeCode GmbH' |
|
54 | __author__ = 'RhodeCode GmbH' |
@@ -47,7 +47,7 b' from rhodecode.lib.vcs.exceptions import' | |||||
47 | from rhodecode.model.changeset_status import ChangesetStatusModel |
|
47 | from rhodecode.model.changeset_status import ChangesetStatusModel | |
48 | from rhodecode.model.comment import CommentsModel |
|
48 | from rhodecode.model.comment import CommentsModel | |
49 | from rhodecode.model.db import ( |
|
49 | from rhodecode.model.db import ( | |
50 | func, or_, PullRequest, ChangesetComment, ChangesetStatus, Repository, |
|
50 | func, false, or_, PullRequest, ChangesetComment, ChangesetStatus, Repository, | |
51 | PullRequestReviewers) |
|
51 | PullRequestReviewers) | |
52 | from rhodecode.model.forms import PullRequestForm |
|
52 | from rhodecode.model.forms import PullRequestForm | |
53 | from rhodecode.model.meta import Session |
|
53 | from rhodecode.model.meta import Session | |
@@ -268,12 +268,14 b' class RepoPullRequestsView(RepoAppView, ' | |||||
268 |
|
268 | |||
269 | return diffset |
|
269 | return diffset | |
270 |
|
270 | |||
271 | def register_comments_vars(self, c, pull_request, versions): |
|
271 | def register_comments_vars(self, c, pull_request, versions, include_drafts=True): | |
272 | comments_model = CommentsModel() |
|
272 | comments_model = CommentsModel() | |
273 |
|
273 | |||
274 | # GENERAL COMMENTS with versions # |
|
274 | # GENERAL COMMENTS with versions # | |
275 | q = comments_model._all_general_comments_of_pull_request(pull_request) |
|
275 | q = comments_model._all_general_comments_of_pull_request(pull_request) | |
276 | q = q.order_by(ChangesetComment.comment_id.asc()) |
|
276 | q = q.order_by(ChangesetComment.comment_id.asc()) | |
|
277 | if not include_drafts: | |||
|
278 | q = q.filter(ChangesetComment.draft == false()) | |||
277 | general_comments = q |
|
279 | general_comments = q | |
278 |
|
280 | |||
279 | # pick comments we want to render at current version |
|
281 | # pick comments we want to render at current version | |
@@ -283,6 +285,8 b' class RepoPullRequestsView(RepoAppView, ' | |||||
283 | # INLINE COMMENTS with versions # |
|
285 | # INLINE COMMENTS with versions # | |
284 | q = comments_model._all_inline_comments_of_pull_request(pull_request) |
|
286 | q = comments_model._all_inline_comments_of_pull_request(pull_request) | |
285 | q = q.order_by(ChangesetComment.comment_id.asc()) |
|
287 | q = q.order_by(ChangesetComment.comment_id.asc()) | |
|
288 | if not include_drafts: | |||
|
289 | q = q.filter(ChangesetComment.draft == false()) | |||
286 | inline_comments = q |
|
290 | inline_comments = q | |
287 |
|
291 | |||
288 | c.inline_versions = comments_model.aggregate_comments( |
|
292 | c.inline_versions = comments_model.aggregate_comments( | |
@@ -1015,7 +1019,7 b' class RepoPullRequestsView(RepoAppView, ' | |||||
1015 | if at_version and at_version != PullRequest.LATEST_VER |
|
1019 | if at_version and at_version != PullRequest.LATEST_VER | |
1016 | else None) |
|
1020 | else None) | |
1017 |
|
1021 | |||
1018 | self.register_comments_vars(c, pull_request_latest, versions) |
|
1022 | self.register_comments_vars(c, pull_request_latest, versions, include_drafts=False) | |
1019 | all_comments = c.inline_comments_flat + c.comments |
|
1023 | all_comments = c.inline_comments_flat + c.comments | |
1020 |
|
1024 | |||
1021 | existing_ids = self._get_existing_ids(self.request.POST) |
|
1025 | existing_ids = self._get_existing_ids(self.request.POST) | |
@@ -1055,9 +1059,9 b' class RepoPullRequestsView(RepoAppView, ' | |||||
1055 | else None) |
|
1059 | else None) | |
1056 |
|
1060 | |||
1057 | c.unresolved_comments = CommentsModel() \ |
|
1061 | c.unresolved_comments = CommentsModel() \ | |
1058 | .get_pull_request_unresolved_todos(pull_request) |
|
1062 | .get_pull_request_unresolved_todos(pull_request, include_drafts=False) | |
1059 | c.resolved_comments = CommentsModel() \ |
|
1063 | c.resolved_comments = CommentsModel() \ | |
1060 | .get_pull_request_resolved_todos(pull_request) |
|
1064 | .get_pull_request_resolved_todos(pull_request, include_drafts=False) | |
1061 |
|
1065 | |||
1062 | all_comments = c.unresolved_comments + c.resolved_comments |
|
1066 | all_comments = c.unresolved_comments + c.resolved_comments | |
1063 | existing_ids = self._get_existing_ids(self.request.POST) |
|
1067 | existing_ids = self._get_existing_ids(self.request.POST) | |
@@ -1544,6 +1548,7 b' class RepoPullRequestsView(RepoAppView, ' | |||||
1544 | status = self.request.POST.get('changeset_status', None) |
|
1548 | status = self.request.POST.get('changeset_status', None) | |
1545 | text = self.request.POST.get('text') |
|
1549 | text = self.request.POST.get('text') | |
1546 | comment_type = self.request.POST.get('comment_type') |
|
1550 | comment_type = self.request.POST.get('comment_type') | |
|
1551 | is_draft = str2bool(self.request.POST.get('draft')) | |||
1547 | resolves_comment_id = self.request.POST.get('resolves_comment_id', None) |
|
1552 | resolves_comment_id = self.request.POST.get('resolves_comment_id', None) | |
1548 | close_pull_request = self.request.POST.get('close_pull_request') |
|
1553 | close_pull_request = self.request.POST.get('close_pull_request') | |
1549 |
|
1554 | |||
@@ -1574,9 +1579,9 b' class RepoPullRequestsView(RepoAppView, ' | |||||
1574 | else: |
|
1579 | else: | |
1575 | # regular comment case, could be inline, or one with status. |
|
1580 | # regular comment case, could be inline, or one with status. | |
1576 | # for that one we check also permissions |
|
1581 | # for that one we check also permissions | |
1577 |
|
1582 | # Additionally ENSURE if somehow draft is sent we're then unable to change status | ||
1578 | allowed_to_change_status = PullRequestModel().check_user_change_status( |
|
1583 | allowed_to_change_status = PullRequestModel().check_user_change_status( | |
1579 | pull_request, self._rhodecode_user) |
|
1584 | pull_request, self._rhodecode_user) and not is_draft | |
1580 |
|
1585 | |||
1581 | if status and allowed_to_change_status: |
|
1586 | if status and allowed_to_change_status: | |
1582 | message = (_('Status change %(transition_icon)s %(status)s') |
|
1587 | message = (_('Status change %(transition_icon)s %(status)s') | |
@@ -1596,8 +1601,10 b' class RepoPullRequestsView(RepoAppView, ' | |||||
1596 | status_change_type=(status |
|
1601 | status_change_type=(status | |
1597 | if status and allowed_to_change_status else None), |
|
1602 | if status and allowed_to_change_status else None), | |
1598 | comment_type=comment_type, |
|
1603 | comment_type=comment_type, | |
|
1604 | is_draft=is_draft, | |||
1599 | resolves_comment_id=resolves_comment_id, |
|
1605 | resolves_comment_id=resolves_comment_id, | |
1600 | auth_user=self._rhodecode_user |
|
1606 | auth_user=self._rhodecode_user, | |
|
1607 | send_email=not is_draft, # skip notification for draft comments | |||
1601 | ) |
|
1608 | ) | |
1602 | is_inline = comment.is_inline |
|
1609 | is_inline = comment.is_inline | |
1603 |
|
1610 | |||
@@ -1638,6 +1645,7 b' class RepoPullRequestsView(RepoAppView, ' | |||||
1638 | 'target_id': h.safeid(h.safe_unicode( |
|
1645 | 'target_id': h.safeid(h.safe_unicode( | |
1639 | self.request.POST.get('f_path'))), |
|
1646 | self.request.POST.get('f_path'))), | |
1640 | } |
|
1647 | } | |
|
1648 | ||||
1641 | if comment: |
|
1649 | if comment: | |
1642 | c.co = comment |
|
1650 | c.co = comment | |
1643 | c.at_version_num = None |
|
1651 | c.at_version_num = None | |
@@ -1648,15 +1656,17 b' class RepoPullRequestsView(RepoAppView, ' | |||||
1648 | data.update(comment.get_dict()) |
|
1656 | data.update(comment.get_dict()) | |
1649 | data.update({'rendered_text': rendered_comment}) |
|
1657 | data.update({'rendered_text': rendered_comment}) | |
1650 |
|
1658 | |||
1651 | comment_broadcast_channel = channelstream.comment_channel( |
|
1659 | # skip channelstream for draft comments | |
1652 | self.db_repo_name, pull_request_obj=pull_request) |
|
1660 | if not is_draft: | |
|
1661 | comment_broadcast_channel = channelstream.comment_channel( | |||
|
1662 | self.db_repo_name, pull_request_obj=pull_request) | |||
1653 |
|
1663 | |||
1654 | comment_data = data |
|
1664 | comment_data = data | |
1655 | comment_type = 'inline' if is_inline else 'general' |
|
1665 | comment_type = 'inline' if is_inline else 'general' | |
1656 | channelstream.comment_channelstream_push( |
|
1666 | channelstream.comment_channelstream_push( | |
1657 | self.request, comment_broadcast_channel, self._rhodecode_user, |
|
1667 | self.request, comment_broadcast_channel, self._rhodecode_user, | |
1658 | _('posted a new {} comment').format(comment_type), |
|
1668 | _('posted a new {} comment').format(comment_type), | |
1659 | comment_data=comment_data) |
|
1669 | comment_data=comment_data) | |
1660 |
|
1670 | |||
1661 | return data |
|
1671 | return data | |
1662 |
|
1672 |
@@ -37,6 +37,7 b' from rhodecode.lib.exceptions import Com' | |||||
37 | from rhodecode.lib.utils2 import extract_mentioned_users, safe_str, safe_int |
|
37 | from rhodecode.lib.utils2 import extract_mentioned_users, safe_str, safe_int | |
38 | from rhodecode.model import BaseModel |
|
38 | from rhodecode.model import BaseModel | |
39 | from rhodecode.model.db import ( |
|
39 | from rhodecode.model.db import ( | |
|
40 | false, | |||
40 | ChangesetComment, |
|
41 | ChangesetComment, | |
41 | User, |
|
42 | User, | |
42 | Notification, |
|
43 | Notification, | |
@@ -160,7 +161,7 b' class CommentsModel(BaseModel):' | |||||
160 |
|
161 | |||
161 | return todos |
|
162 | return todos | |
162 |
|
163 | |||
163 | def get_pull_request_unresolved_todos(self, pull_request, show_outdated=True): |
|
164 | def get_pull_request_unresolved_todos(self, pull_request, show_outdated=True, include_drafts=True): | |
164 |
|
165 | |||
165 | todos = Session().query(ChangesetComment) \ |
|
166 | todos = Session().query(ChangesetComment) \ | |
166 | .filter(ChangesetComment.pull_request == pull_request) \ |
|
167 | .filter(ChangesetComment.pull_request == pull_request) \ | |
@@ -168,6 +169,9 b' class CommentsModel(BaseModel):' | |||||
168 | .filter(ChangesetComment.comment_type |
|
169 | .filter(ChangesetComment.comment_type | |
169 | == ChangesetComment.COMMENT_TYPE_TODO) |
|
170 | == ChangesetComment.COMMENT_TYPE_TODO) | |
170 |
|
171 | |||
|
172 | if not include_drafts: | |||
|
173 | todos = todos.filter(ChangesetComment.draft == false()) | |||
|
174 | ||||
171 | if not show_outdated: |
|
175 | if not show_outdated: | |
172 | todos = todos.filter( |
|
176 | todos = todos.filter( | |
173 | coalesce(ChangesetComment.display_state, '') != |
|
177 | coalesce(ChangesetComment.display_state, '') != | |
@@ -177,7 +181,7 b' class CommentsModel(BaseModel):' | |||||
177 |
|
181 | |||
178 | return todos |
|
182 | return todos | |
179 |
|
183 | |||
180 | def get_pull_request_resolved_todos(self, pull_request, show_outdated=True): |
|
184 | def get_pull_request_resolved_todos(self, pull_request, show_outdated=True, include_drafts=True): | |
181 |
|
185 | |||
182 | todos = Session().query(ChangesetComment) \ |
|
186 | todos = Session().query(ChangesetComment) \ | |
183 | .filter(ChangesetComment.pull_request == pull_request) \ |
|
187 | .filter(ChangesetComment.pull_request == pull_request) \ | |
@@ -185,6 +189,9 b' class CommentsModel(BaseModel):' | |||||
185 | .filter(ChangesetComment.comment_type |
|
189 | .filter(ChangesetComment.comment_type | |
186 | == ChangesetComment.COMMENT_TYPE_TODO) |
|
190 | == ChangesetComment.COMMENT_TYPE_TODO) | |
187 |
|
191 | |||
|
192 | if not include_drafts: | |||
|
193 | todos = todos.filter(ChangesetComment.draft == false()) | |||
|
194 | ||||
188 | if not show_outdated: |
|
195 | if not show_outdated: | |
189 | todos = todos.filter( |
|
196 | todos = todos.filter( | |
190 | coalesce(ChangesetComment.display_state, '') != |
|
197 | coalesce(ChangesetComment.display_state, '') != | |
@@ -194,7 +201,7 b' class CommentsModel(BaseModel):' | |||||
194 |
|
201 | |||
195 | return todos |
|
202 | return todos | |
196 |
|
203 | |||
197 | def get_commit_unresolved_todos(self, commit_id, show_outdated=True): |
|
204 | def get_commit_unresolved_todos(self, commit_id, show_outdated=True, include_drafts=True): | |
198 |
|
205 | |||
199 | todos = Session().query(ChangesetComment) \ |
|
206 | todos = Session().query(ChangesetComment) \ | |
200 | .filter(ChangesetComment.revision == commit_id) \ |
|
207 | .filter(ChangesetComment.revision == commit_id) \ | |
@@ -202,6 +209,9 b' class CommentsModel(BaseModel):' | |||||
202 | .filter(ChangesetComment.comment_type |
|
209 | .filter(ChangesetComment.comment_type | |
203 | == ChangesetComment.COMMENT_TYPE_TODO) |
|
210 | == ChangesetComment.COMMENT_TYPE_TODO) | |
204 |
|
211 | |||
|
212 | if not include_drafts: | |||
|
213 | todos = todos.filter(ChangesetComment.draft == false()) | |||
|
214 | ||||
205 | if not show_outdated: |
|
215 | if not show_outdated: | |
206 | todos = todos.filter( |
|
216 | todos = todos.filter( | |
207 | coalesce(ChangesetComment.display_state, '') != |
|
217 | coalesce(ChangesetComment.display_state, '') != | |
@@ -211,7 +221,7 b' class CommentsModel(BaseModel):' | |||||
211 |
|
221 | |||
212 | return todos |
|
222 | return todos | |
213 |
|
223 | |||
214 | def get_commit_resolved_todos(self, commit_id, show_outdated=True): |
|
224 | def get_commit_resolved_todos(self, commit_id, show_outdated=True, include_drafts=True): | |
215 |
|
225 | |||
216 | todos = Session().query(ChangesetComment) \ |
|
226 | todos = Session().query(ChangesetComment) \ | |
217 | .filter(ChangesetComment.revision == commit_id) \ |
|
227 | .filter(ChangesetComment.revision == commit_id) \ | |
@@ -219,6 +229,9 b' class CommentsModel(BaseModel):' | |||||
219 | .filter(ChangesetComment.comment_type |
|
229 | .filter(ChangesetComment.comment_type | |
220 | == ChangesetComment.COMMENT_TYPE_TODO) |
|
230 | == ChangesetComment.COMMENT_TYPE_TODO) | |
221 |
|
231 | |||
|
232 | if not include_drafts: | |||
|
233 | todos = todos.filter(ChangesetComment.draft == false()) | |||
|
234 | ||||
222 | if not show_outdated: |
|
235 | if not show_outdated: | |
223 | todos = todos.filter( |
|
236 | todos = todos.filter( | |
224 | coalesce(ChangesetComment.display_state, '') != |
|
237 | coalesce(ChangesetComment.display_state, '') != | |
@@ -228,11 +241,15 b' class CommentsModel(BaseModel):' | |||||
228 |
|
241 | |||
229 | return todos |
|
242 | return todos | |
230 |
|
243 | |||
231 | def get_commit_inline_comments(self, commit_id): |
|
244 | def get_commit_inline_comments(self, commit_id, include_drafts=True): | |
232 | inline_comments = Session().query(ChangesetComment) \ |
|
245 | inline_comments = Session().query(ChangesetComment) \ | |
233 | .filter(ChangesetComment.line_no != None) \ |
|
246 | .filter(ChangesetComment.line_no != None) \ | |
234 | .filter(ChangesetComment.f_path != None) \ |
|
247 | .filter(ChangesetComment.f_path != None) \ | |
235 | .filter(ChangesetComment.revision == commit_id) |
|
248 | .filter(ChangesetComment.revision == commit_id) | |
|
249 | ||||
|
250 | if not include_drafts: | |||
|
251 | inline_comments = inline_comments.filter(ChangesetComment.draft == false()) | |||
|
252 | ||||
236 | inline_comments = inline_comments.all() |
|
253 | inline_comments = inline_comments.all() | |
237 | return inline_comments |
|
254 | return inline_comments | |
238 |
|
255 | |||
@@ -245,7 +262,7 b' class CommentsModel(BaseModel):' | |||||
245 |
|
262 | |||
246 | def create(self, text, repo, user, commit_id=None, pull_request=None, |
|
263 | def create(self, text, repo, user, commit_id=None, pull_request=None, | |
247 | f_path=None, line_no=None, status_change=None, |
|
264 | f_path=None, line_no=None, status_change=None, | |
248 | status_change_type=None, comment_type=None, |
|
265 | status_change_type=None, comment_type=None, is_draft=False, | |
249 | resolves_comment_id=None, closing_pr=False, send_email=True, |
|
266 | resolves_comment_id=None, closing_pr=False, send_email=True, | |
250 | renderer=None, auth_user=None, extra_recipients=None): |
|
267 | renderer=None, auth_user=None, extra_recipients=None): | |
251 | """ |
|
268 | """ | |
@@ -262,6 +279,7 b' class CommentsModel(BaseModel):' | |||||
262 | :param line_no: |
|
279 | :param line_no: | |
263 | :param status_change: Label for status change |
|
280 | :param status_change: Label for status change | |
264 | :param comment_type: Type of comment |
|
281 | :param comment_type: Type of comment | |
|
282 | :param is_draft: is comment a draft only | |||
265 | :param resolves_comment_id: id of comment which this one will resolve |
|
283 | :param resolves_comment_id: id of comment which this one will resolve | |
266 | :param status_change_type: type of status change |
|
284 | :param status_change_type: type of status change | |
267 | :param closing_pr: |
|
285 | :param closing_pr: | |
@@ -288,6 +306,7 b' class CommentsModel(BaseModel):' | |||||
288 | validated_kwargs = schema.deserialize(dict( |
|
306 | validated_kwargs = schema.deserialize(dict( | |
289 | comment_body=text, |
|
307 | comment_body=text, | |
290 | comment_type=comment_type, |
|
308 | comment_type=comment_type, | |
|
309 | is_draft=is_draft, | |||
291 | comment_file=f_path, |
|
310 | comment_file=f_path, | |
292 | comment_line=line_no, |
|
311 | comment_line=line_no, | |
293 | renderer_type=renderer, |
|
312 | renderer_type=renderer, | |
@@ -296,6 +315,7 b' class CommentsModel(BaseModel):' | |||||
296 | repo=repo.repo_id, |
|
315 | repo=repo.repo_id, | |
297 | user=user.user_id, |
|
316 | user=user.user_id, | |
298 | )) |
|
317 | )) | |
|
318 | is_draft = validated_kwargs['is_draft'] | |||
299 |
|
319 | |||
300 | comment = ChangesetComment() |
|
320 | comment = ChangesetComment() | |
301 | comment.renderer = validated_kwargs['renderer_type'] |
|
321 | comment.renderer = validated_kwargs['renderer_type'] | |
@@ -303,6 +323,7 b' class CommentsModel(BaseModel):' | |||||
303 | comment.f_path = validated_kwargs['comment_file'] |
|
323 | comment.f_path = validated_kwargs['comment_file'] | |
304 | comment.line_no = validated_kwargs['comment_line'] |
|
324 | comment.line_no = validated_kwargs['comment_line'] | |
305 | comment.comment_type = validated_kwargs['comment_type'] |
|
325 | comment.comment_type = validated_kwargs['comment_type'] | |
|
326 | comment.draft = is_draft | |||
306 |
|
327 | |||
307 | comment.repo = repo |
|
328 | comment.repo = repo | |
308 | comment.author = user |
|
329 | comment.author = user | |
@@ -462,10 +483,11 b' class CommentsModel(BaseModel):' | |||||
462 | else: |
|
483 | else: | |
463 | action = 'repo.commit.comment.create' |
|
484 | action = 'repo.commit.comment.create' | |
464 |
|
485 | |||
465 | comment_data = comment.get_api_data() |
|
486 | if not is_draft: | |
|
487 | comment_data = comment.get_api_data() | |||
466 |
|
488 | |||
467 | self._log_audit_action( |
|
489 | self._log_audit_action( | |
468 | action, {'data': comment_data}, auth_user, comment) |
|
490 | action, {'data': comment_data}, auth_user, comment) | |
469 |
|
491 | |||
470 | return comment |
|
492 | return comment | |
471 |
|
493 |
@@ -3767,6 +3767,7 b' class ChangesetComment(Base, BaseModel):' | |||||
3767 | renderer = Column('renderer', Unicode(64), nullable=True) |
|
3767 | renderer = Column('renderer', Unicode(64), nullable=True) | |
3768 | display_state = Column('display_state', Unicode(128), nullable=True) |
|
3768 | display_state = Column('display_state', Unicode(128), nullable=True) | |
3769 | immutable_state = Column('immutable_state', Unicode(128), nullable=True, default=OP_CHANGEABLE) |
|
3769 | immutable_state = Column('immutable_state', Unicode(128), nullable=True, default=OP_CHANGEABLE) | |
|
3770 | draft = Column('draft', Boolean(), nullable=True, default=False) | |||
3770 |
|
3771 | |||
3771 | comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE) |
|
3772 | comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE) | |
3772 | resolved_comment_id = Column('resolved_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=True) |
|
3773 | resolved_comment_id = Column('resolved_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=True) |
@@ -60,7 +60,7 b' class CommentSchema(colander.MappingSche' | |||||
60 | colander.String(), |
|
60 | colander.String(), | |
61 | validator=colander.OneOf(ChangesetComment.COMMENT_TYPES), |
|
61 | validator=colander.OneOf(ChangesetComment.COMMENT_TYPES), | |
62 | missing=ChangesetComment.COMMENT_TYPE_NOTE) |
|
62 | missing=ChangesetComment.COMMENT_TYPE_NOTE) | |
63 |
|
63 | is_draft = colander.SchemaNode(colander.Boolean(),missing=False) | ||
64 | comment_file = colander.SchemaNode(colander.String(), missing=None) |
|
64 | comment_file = colander.SchemaNode(colander.String(), missing=None) | |
65 | comment_line = colander.SchemaNode(colander.String(), missing=None) |
|
65 | comment_line = colander.SchemaNode(colander.String(), missing=None) | |
66 | status_change = colander.SchemaNode( |
|
66 | status_change = colander.SchemaNode( |
@@ -162,7 +162,6 b' input[type="button"] {' | |||||
162 | } |
|
162 | } | |
163 | } |
|
163 | } | |
164 |
|
164 | |||
165 | .btn-warning, |
|
|||
166 | .btn-danger, |
|
165 | .btn-danger, | |
167 | .revoke_perm, |
|
166 | .revoke_perm, | |
168 | .btn-x, |
|
167 | .btn-x, | |
@@ -196,6 +195,36 b' input[type="button"] {' | |||||
196 | } |
|
195 | } | |
197 | } |
|
196 | } | |
198 |
|
197 | |||
|
198 | .btn-warning { | |||
|
199 | .border ( @border-thickness, @alert3 ); | |||
|
200 | background-color: white; | |||
|
201 | color: @alert3; | |||
|
202 | ||||
|
203 | a { | |||
|
204 | color: @alert3; | |||
|
205 | } | |||
|
206 | ||||
|
207 | &:hover, | |||
|
208 | &.active { | |||
|
209 | .border ( @border-thickness, @alert3 ); | |||
|
210 | color: white; | |||
|
211 | background-color: @alert3; | |||
|
212 | ||||
|
213 | a { | |||
|
214 | color: white; | |||
|
215 | } | |||
|
216 | } | |||
|
217 | ||||
|
218 | i { | |||
|
219 | display:none; | |||
|
220 | } | |||
|
221 | ||||
|
222 | &:disabled { | |||
|
223 | background-color: white; | |||
|
224 | color: @alert3; | |||
|
225 | } | |||
|
226 | } | |||
|
227 | ||||
199 | .btn-approved-status { |
|
228 | .btn-approved-status { | |
200 | .border ( @border-thickness, @alert1 ); |
|
229 | .border ( @border-thickness, @alert1 ); | |
201 | background-color: white; |
|
230 | background-color: white; | |
@@ -401,6 +430,37 b' input[type="button"] {' | |||||
401 | } |
|
430 | } | |
402 |
|
431 | |||
403 |
|
432 | |||
|
433 | input[type="submit"].btn-warning { | |||
|
434 | &:extend(.btn-warning); | |||
|
435 | ||||
|
436 | &:focus { | |||
|
437 | outline: 0; | |||
|
438 | } | |||
|
439 | ||||
|
440 | &:hover { | |||
|
441 | &:extend(.btn-warning:hover); | |||
|
442 | } | |||
|
443 | ||||
|
444 | &.btn-link { | |||
|
445 | &:extend(.btn-link); | |||
|
446 | color: @alert3; | |||
|
447 | ||||
|
448 | &:disabled { | |||
|
449 | color: @alert3; | |||
|
450 | background-color: transparent; | |||
|
451 | } | |||
|
452 | } | |||
|
453 | ||||
|
454 | &:disabled { | |||
|
455 | .border ( @border-thickness-buttons, @alert3 ); | |||
|
456 | background-color: white; | |||
|
457 | color: @alert3; | |||
|
458 | opacity: 0.5; | |||
|
459 | } | |||
|
460 | } | |||
|
461 | ||||
|
462 | ||||
|
463 | ||||
404 | // TODO: johbo: Form button tweaks, check if we can use the classes instead |
|
464 | // TODO: johbo: Form button tweaks, check if we can use the classes instead | |
405 | input[type="submit"] { |
|
465 | input[type="submit"] { | |
406 | &:extend(.btn-primary); |
|
466 | &:extend(.btn-primary); |
@@ -61,6 +61,13 b' tr.inline-comments div {' | |||||
61 | visibility: hidden; |
|
61 | visibility: hidden; | |
62 | } |
|
62 | } | |
63 |
|
63 | |||
|
64 | .comment-draft { | |||
|
65 | float: left; | |||
|
66 | margin-right: 10px; | |||
|
67 | font-weight: 600; | |||
|
68 | color: @alert3; | |||
|
69 | } | |||
|
70 | ||||
64 | .comment-label { |
|
71 | .comment-label { | |
65 | float: left; |
|
72 | float: left; | |
66 |
|
73 |
@@ -349,7 +349,12 b' var initCommentBoxCodeMirror = function(' | |||||
349 | }; |
|
349 | }; | |
350 |
|
350 | |||
351 | var submitForm = function(cm, pred) { |
|
351 | var submitForm = function(cm, pred) { | |
352 | $(cm.display.input.textarea.form).submit(); |
|
352 | $(cm.display.input.textarea.form).find('.submit-comment-action').click(); | |
|
353 | return CodeMirror.Pass; | |||
|
354 | }; | |||
|
355 | ||||
|
356 | var submitFormAsDraft = function(cm, pred) { | |||
|
357 | $(cm.display.input.textarea.form).find('.submit-draft-action').click(); | |||
353 | return CodeMirror.Pass; |
|
358 | return CodeMirror.Pass; | |
354 | }; |
|
359 | }; | |
355 |
|
360 | |||
@@ -475,9 +480,11 b' var initCommentBoxCodeMirror = function(' | |||||
475 | // submit form on Meta-Enter |
|
480 | // submit form on Meta-Enter | |
476 | if (OSType === "mac") { |
|
481 | if (OSType === "mac") { | |
477 | extraKeys["Cmd-Enter"] = submitForm; |
|
482 | extraKeys["Cmd-Enter"] = submitForm; | |
|
483 | extraKeys["Shift-Cmd-Enter"] = submitFormAsDraft; | |||
478 | } |
|
484 | } | |
479 | else { |
|
485 | else { | |
480 | extraKeys["Ctrl-Enter"] = submitForm; |
|
486 | extraKeys["Ctrl-Enter"] = submitForm; | |
|
487 | extraKeys["Shift-Ctrl-Enter"] = submitFormAsDraft; | |||
481 | } |
|
488 | } | |
482 |
|
489 | |||
483 | if (triggerActions) { |
|
490 | if (triggerActions) { |
@@ -124,16 +124,20 b' var _submitAjaxPOST = function(url, post' | |||||
124 | this.statusChange = this.withLineNo('#change_status'); |
|
124 | this.statusChange = this.withLineNo('#change_status'); | |
125 |
|
125 | |||
126 | this.submitForm = formElement; |
|
126 | this.submitForm = formElement; | |
127 | this.submitButton = $(this.submitForm).find('input[type="submit"]'); |
|
127 | ||
|
128 | this.submitButton = $(this.submitForm).find('.submit-comment-action'); | |||
128 | this.submitButtonText = this.submitButton.val(); |
|
129 | this.submitButtonText = this.submitButton.val(); | |
129 |
|
130 | |||
|
131 | this.submitDraftButton = $(this.submitForm).find('.submit-draft-action'); | |||
|
132 | this.submitDraftButtonText = this.submitDraftButton.val(); | |||
130 |
|
133 | |||
131 | this.previewUrl = pyroutes.url('repo_commit_comment_preview', |
|
134 | this.previewUrl = pyroutes.url('repo_commit_comment_preview', | |
132 | {'repo_name': templateContext.repo_name, |
|
135 | {'repo_name': templateContext.repo_name, | |
133 | 'commit_id': templateContext.commit_data.commit_id}); |
|
136 | 'commit_id': templateContext.commit_data.commit_id}); | |
134 |
|
137 | |||
135 | if (edit){ |
|
138 | if (edit){ | |
136 |
this.submitButton |
|
139 | this.submitDraftButton.hide(); | |
|
140 | this.submitButtonText = _gettext('Update Comment'); | |||
137 | $(this.commentType).prop('disabled', true); |
|
141 | $(this.commentType).prop('disabled', true); | |
138 | $(this.commentType).addClass('disabled'); |
|
142 | $(this.commentType).addClass('disabled'); | |
139 | var editInfo = |
|
143 | var editInfo = | |
@@ -215,10 +219,17 b' var _submitAjaxPOST = function(url, post' | |||||
215 | this.getCommentStatus = function() { |
|
219 | this.getCommentStatus = function() { | |
216 | return $(this.submitForm).find(this.statusChange).val(); |
|
220 | return $(this.submitForm).find(this.statusChange).val(); | |
217 | }; |
|
221 | }; | |
|
222 | ||||
218 | this.getCommentType = function() { |
|
223 | this.getCommentType = function() { | |
219 | return $(this.submitForm).find(this.commentType).val(); |
|
224 | return $(this.submitForm).find(this.commentType).val(); | |
220 | }; |
|
225 | }; | |
221 |
|
226 | |||
|
227 | this.getDraftState = function () { | |||
|
228 | var submitterElem = $(this.submitForm).find('input[type="submit"].submitter'); | |||
|
229 | var data = $(submitterElem).data('isDraft'); | |||
|
230 | return data | |||
|
231 | } | |||
|
232 | ||||
222 | this.getResolvesId = function() { |
|
233 | this.getResolvesId = function() { | |
223 | return $(this.submitForm).find(this.resolvesId).val() || null; |
|
234 | return $(this.submitForm).find(this.resolvesId).val() || null; | |
224 | }; |
|
235 | }; | |
@@ -233,7 +244,9 b' var _submitAjaxPOST = function(url, post' | |||||
233 | }; |
|
244 | }; | |
234 |
|
245 | |||
235 | this.isAllowedToSubmit = function() { |
|
246 | this.isAllowedToSubmit = function() { | |
236 |
r |
|
247 | var commentDisabled = $(this.submitButton).prop('disabled'); | |
|
248 | var draftDisabled = $(this.submitDraftButton).prop('disabled'); | |||
|
249 | return !commentDisabled && !draftDisabled; | |||
237 | }; |
|
250 | }; | |
238 |
|
251 | |||
239 | this.initStatusChangeSelector = function(){ |
|
252 | this.initStatusChangeSelector = function(){ | |
@@ -259,11 +272,13 b' var _submitAjaxPOST = function(url, post' | |||||
259 | dropdownAutoWidth: true, |
|
272 | dropdownAutoWidth: true, | |
260 | minimumResultsForSearch: -1 |
|
273 | minimumResultsForSearch: -1 | |
261 | }); |
|
274 | }); | |
|
275 | ||||
262 | $(this.submitForm).find(this.statusChange).on('change', function() { |
|
276 | $(this.submitForm).find(this.statusChange).on('change', function() { | |
263 | var status = self.getCommentStatus(); |
|
277 | var status = self.getCommentStatus(); | |
264 |
|
278 | |||
265 | if (status && !self.isInline()) { |
|
279 | if (status && !self.isInline()) { | |
266 | $(self.submitButton).prop('disabled', false); |
|
280 | $(self.submitButton).prop('disabled', false); | |
|
281 | $(self.submitDraftButton).prop('disabled', false); | |||
267 | } |
|
282 | } | |
268 |
|
283 | |||
269 | var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status); |
|
284 | var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status); | |
@@ -295,10 +310,10 b' var _submitAjaxPOST = function(url, post' | |||||
295 | $(this.statusChange).select2('readonly', false); |
|
310 | $(this.statusChange).select2('readonly', false); | |
296 | }; |
|
311 | }; | |
297 |
|
312 | |||
298 | this.globalSubmitSuccessCallback = function(){ |
|
313 | this.globalSubmitSuccessCallback = function(comment){ | |
299 | // default behaviour is to call GLOBAL hook, if it's registered. |
|
314 | // default behaviour is to call GLOBAL hook, if it's registered. | |
300 | if (window.commentFormGlobalSubmitSuccessCallback !== undefined){ |
|
315 | if (window.commentFormGlobalSubmitSuccessCallback !== undefined){ | |
301 | commentFormGlobalSubmitSuccessCallback(); |
|
316 | commentFormGlobalSubmitSuccessCallback(comment); | |
302 | } |
|
317 | } | |
303 | }; |
|
318 | }; | |
304 |
|
319 | |||
@@ -321,6 +336,7 b' var _submitAjaxPOST = function(url, post' | |||||
321 | var text = self.cm.getValue(); |
|
336 | var text = self.cm.getValue(); | |
322 | var status = self.getCommentStatus(); |
|
337 | var status = self.getCommentStatus(); | |
323 | var commentType = self.getCommentType(); |
|
338 | var commentType = self.getCommentType(); | |
|
339 | var isDraft = self.getDraftState(); | |||
324 | var resolvesCommentId = self.getResolvesId(); |
|
340 | var resolvesCommentId = self.getResolvesId(); | |
325 | var closePullRequest = self.getClosePr(); |
|
341 | var closePullRequest = self.getClosePr(); | |
326 |
|
342 | |||
@@ -365,7 +381,7 b' var _submitAjaxPOST = function(url, post' | |||||
365 | } |
|
381 | } | |
366 |
|
382 | |||
367 | // run global callback on submit |
|
383 | // run global callback on submit | |
368 | self.globalSubmitSuccessCallback(); |
|
384 | self.globalSubmitSuccessCallback({draft: isDraft, comment_id: comment_id}); | |
369 |
|
385 | |||
370 | }; |
|
386 | }; | |
371 | var submitFailCallback = function(jqXHR, textStatus, errorThrown) { |
|
387 | var submitFailCallback = function(jqXHR, textStatus, errorThrown) { | |
@@ -409,10 +425,20 b' var _submitAjaxPOST = function(url, post' | |||||
409 | } |
|
425 | } | |
410 |
|
426 | |||
411 | $(this.submitButton).prop('disabled', submitState); |
|
427 | $(this.submitButton).prop('disabled', submitState); | |
|
428 | $(this.submitDraftButton).prop('disabled', submitState); | |||
|
429 | ||||
412 | if (submitEvent) { |
|
430 | if (submitEvent) { | |
413 | $(this.submitButton).val(_gettext('Submitting...')); |
|
431 | var isDraft = self.getDraftState(); | |
|
432 | ||||
|
433 | if (isDraft) { | |||
|
434 | $(this.submitDraftButton).val(_gettext('Saving Draft...')); | |||
|
435 | } else { | |||
|
436 | $(this.submitButton).val(_gettext('Submitting...')); | |||
|
437 | } | |||
|
438 | ||||
414 | } else { |
|
439 | } else { | |
415 | $(this.submitButton).val(this.submitButtonText); |
|
440 | $(this.submitButton).val(this.submitButtonText); | |
|
441 | $(this.submitDraftButton).val(this.submitDraftButtonText); | |||
416 | } |
|
442 | } | |
417 |
|
443 | |||
418 | }; |
|
444 | }; | |
@@ -488,6 +514,7 b' var _submitAjaxPOST = function(url, post' | |||||
488 | if (!allowedToSubmit){ |
|
514 | if (!allowedToSubmit){ | |
489 | return false; |
|
515 | return false; | |
490 | } |
|
516 | } | |
|
517 | ||||
491 | self.handleFormSubmit(); |
|
518 | self.handleFormSubmit(); | |
492 | }); |
|
519 | }); | |
493 |
|
520 | |||
@@ -659,7 +686,8 b' var CommentsController = function() {' | |||||
659 | var $node = $(node); |
|
686 | var $node = $(node); | |
660 | var $td = $node.closest('td'); |
|
687 | var $td = $node.closest('td'); | |
661 | var $comment = $node.closest('.comment'); |
|
688 | var $comment = $node.closest('.comment'); | |
662 |
var comment_id = $comment. |
|
689 | var comment_id = $($comment).data('commentId'); | |
|
690 | var isDraft = $($comment).data('commentDraft'); | |||
663 | var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id); |
|
691 | var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id); | |
664 | var postData = { |
|
692 | var postData = { | |
665 | 'csrf_token': CSRF_TOKEN |
|
693 | 'csrf_token': CSRF_TOKEN | |
@@ -677,7 +705,7 b' var CommentsController = function() {' | |||||
677 | updateSticky() |
|
705 | updateSticky() | |
678 | } |
|
706 | } | |
679 |
|
707 | |||
680 | if (window.refreshAllComments !== undefined) { |
|
708 | if (window.refreshAllComments !== undefined && !isDraft) { | |
681 | // if we have this handler, run it, and refresh all comments boxes |
|
709 | // if we have this handler, run it, and refresh all comments boxes | |
682 | refreshAllComments() |
|
710 | refreshAllComments() | |
683 | } |
|
711 | } | |
@@ -716,6 +744,25 b' var CommentsController = function() {' | |||||
716 | }) |
|
744 | }) | |
717 | }; |
|
745 | }; | |
718 |
|
746 | |||
|
747 | this._finalizeDrafts = function(commentIds) { | |||
|
748 | window.finalizeDrafts(commentIds) | |||
|
749 | } | |||
|
750 | ||||
|
751 | this.finalizeDrafts = function(commentIds) { | |||
|
752 | ||||
|
753 | SwalNoAnimation.fire({ | |||
|
754 | title: _ngettext('Submit {0} draft comment', 'Submit {0} draft comments', commentIds.length).format(commentIds.length), | |||
|
755 | icon: 'warning', | |||
|
756 | showCancelButton: true, | |||
|
757 | confirmButtonText: _gettext('Yes, finalize drafts'), | |||
|
758 | ||||
|
759 | }).then(function(result) { | |||
|
760 | if (result.value) { | |||
|
761 | self._finalizeDrafts(commentIds); | |||
|
762 | } | |||
|
763 | }) | |||
|
764 | }; | |||
|
765 | ||||
719 | this.toggleWideMode = function (node) { |
|
766 | this.toggleWideMode = function (node) { | |
720 | if ($('#content').hasClass('wrapper')) { |
|
767 | if ($('#content').hasClass('wrapper')) { | |
721 | $('#content').removeClass("wrapper"); |
|
768 | $('#content').removeClass("wrapper"); | |
@@ -916,7 +963,8 b' var CommentsController = function() {' | |||||
916 | this.editComment = function(node) { |
|
963 | this.editComment = function(node) { | |
917 | var $node = $(node); |
|
964 | var $node = $(node); | |
918 | var $comment = $(node).closest('.comment'); |
|
965 | var $comment = $(node).closest('.comment'); | |
919 |
var comment_id = $comment. |
|
966 | var comment_id = $($comment).data('commentId'); | |
|
967 | var isDraft = $($comment).data('commentDraft'); | |||
920 | var $form = null |
|
968 | var $form = null | |
921 |
|
969 | |||
922 | var $comments = $node.closest('div.inline-comments'); |
|
970 | var $comments = $node.closest('div.inline-comments'); | |
@@ -1002,6 +1050,7 b' var CommentsController = function() {' | |||||
1002 | 'f_path': f_path, |
|
1050 | 'f_path': f_path, | |
1003 | 'line': lineno, |
|
1051 | 'line': lineno, | |
1004 | 'comment_type': commentType, |
|
1052 | 'comment_type': commentType, | |
|
1053 | 'draft': isDraft, | |||
1005 | 'version': version, |
|
1054 | 'version': version, | |
1006 | 'csrf_token': CSRF_TOKEN |
|
1055 | 'csrf_token': CSRF_TOKEN | |
1007 | }; |
|
1056 | }; | |
@@ -1084,7 +1133,7 b' var CommentsController = function() {' | |||||
1084 | $comments.find('.cb-comment-add-button').before(html); |
|
1133 | $comments.find('.cb-comment-add-button').before(html); | |
1085 |
|
1134 | |||
1086 | // run global callback on submit |
|
1135 | // run global callback on submit | |
1087 | commentForm.globalSubmitSuccessCallback(); |
|
1136 | commentForm.globalSubmitSuccessCallback({draft: isDraft, comment_id: comment_id}); | |
1088 |
|
1137 | |||
1089 | } catch (e) { |
|
1138 | } catch (e) { | |
1090 | console.error(e); |
|
1139 | console.error(e); | |
@@ -1101,7 +1150,7 b' var CommentsController = function() {' | |||||
1101 | updateSticky() |
|
1150 | updateSticky() | |
1102 | } |
|
1151 | } | |
1103 |
|
1152 | |||
1104 | if (window.refreshAllComments !== undefined) { |
|
1153 | if (window.refreshAllComments !== undefined && !isDraft) { | |
1105 | // if we have this handler, run it, and refresh all comments boxes |
|
1154 | // if we have this handler, run it, and refresh all comments boxes | |
1106 | refreshAllComments() |
|
1155 | refreshAllComments() | |
1107 | } |
|
1156 | } | |
@@ -1178,6 +1227,7 b' var CommentsController = function() {' | |||||
1178 | var text = commentForm.cm.getValue(); |
|
1227 | var text = commentForm.cm.getValue(); | |
1179 | var commentType = commentForm.getCommentType(); |
|
1228 | var commentType = commentForm.getCommentType(); | |
1180 | var resolvesCommentId = commentForm.getResolvesId(); |
|
1229 | var resolvesCommentId = commentForm.getResolvesId(); | |
|
1230 | var isDraft = commentForm.getDraftState(); | |||
1181 |
|
1231 | |||
1182 | if (text === "") { |
|
1232 | if (text === "") { | |
1183 | return; |
|
1233 | return; | |
@@ -1201,6 +1251,7 b' var CommentsController = function() {' | |||||
1201 | 'f_path': f_path, |
|
1251 | 'f_path': f_path, | |
1202 | 'line': lineno, |
|
1252 | 'line': lineno, | |
1203 | 'comment_type': commentType, |
|
1253 | 'comment_type': commentType, | |
|
1254 | 'draft': isDraft, | |||
1204 | 'csrf_token': CSRF_TOKEN |
|
1255 | 'csrf_token': CSRF_TOKEN | |
1205 | }; |
|
1256 | }; | |
1206 | if (resolvesCommentId){ |
|
1257 | if (resolvesCommentId){ | |
@@ -1222,7 +1273,7 b' var CommentsController = function() {' | |||||
1222 | } |
|
1273 | } | |
1223 |
|
1274 | |||
1224 | // run global callback on submit |
|
1275 | // run global callback on submit | |
1225 | commentForm.globalSubmitSuccessCallback(); |
|
1276 | commentForm.globalSubmitSuccessCallback({draft: isDraft, comment_id: comment_id}); | |
1226 |
|
1277 | |||
1227 | } catch (e) { |
|
1278 | } catch (e) { | |
1228 | console.error(e); |
|
1279 | console.error(e); | |
@@ -1239,7 +1290,7 b' var CommentsController = function() {' | |||||
1239 | updateSticky() |
|
1290 | updateSticky() | |
1240 | } |
|
1291 | } | |
1241 |
|
1292 | |||
1242 | if (window.refreshAllComments !== undefined) { |
|
1293 | if (window.refreshAllComments !== undefined && !isDraft) { | |
1243 | // if we have this handler, run it, and refresh all comments boxes |
|
1294 | // if we have this handler, run it, and refresh all comments boxes | |
1244 | refreshAllComments() |
|
1295 | refreshAllComments() | |
1245 | } |
|
1296 | } |
@@ -1225,6 +1225,14 b'' | |||||
1225 | (function () { |
|
1225 | (function () { | |
1226 | "use sctrict"; |
|
1226 | "use sctrict"; | |
1227 |
|
1227 | |||
|
1228 | // details block auto-hide menu | |||
|
1229 | $(document).mouseup(function(e) { | |||
|
1230 | var container = $('.details-inline-block'); | |||
|
1231 | if (!container.is(e.target) && container.has(e.target).length === 0) { | |||
|
1232 | $('.details-inline-block[open]').removeAttr('open') | |||
|
1233 | } | |||
|
1234 | }); | |||
|
1235 | ||||
1228 | var $sideBar = $('.right-sidebar'); |
|
1236 | var $sideBar = $('.right-sidebar'); | |
1229 | var expanded = $sideBar.hasClass('right-sidebar-expanded'); |
|
1237 | var expanded = $sideBar.hasClass('right-sidebar-expanded'); | |
1230 | var sidebarState = templateContext.session_attrs.sidebarState; |
|
1238 | var sidebarState = templateContext.session_attrs.sidebarState; |
@@ -31,8 +31,12 b'' | |||||
31 | <% |
|
31 | <% | |
32 | display = '' |
|
32 | display = '' | |
33 | _cls = '' |
|
33 | _cls = '' | |
|
34 | ## Extra precaution to not show drafts in the sidebar for todo/comments | |||
|
35 | if comment_obj.draft: | |||
|
36 | continue | |||
34 | %> |
|
37 | %> | |
35 |
|
38 | |||
|
39 | ||||
36 | <% |
|
40 | <% | |
37 | comment_ver_index = comment_obj.get_index_version(getattr(c, 'versions', [])) |
|
41 | comment_ver_index = comment_obj.get_index_version(getattr(c, 'versions', [])) | |
38 | prev_comment_ver_index = 0 |
|
42 | prev_comment_ver_index = 0 |
@@ -3,20 +3,25 b'' | |||||
3 | ## <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/> |
|
3 | ## <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/> | |
4 | ## ${comment.comment_block(comment)} |
|
4 | ## ${comment.comment_block(comment)} | |
5 | ## |
|
5 | ## | |
|
6 | <%namespace name="base" file="/base/base.mako"/> | |||
6 |
|
|
7 | ||
7 | <%! |
|
8 | <%! | |
8 | from rhodecode.lib import html_filters |
|
9 | from rhodecode.lib import html_filters | |
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 | <%def name="comment_block(comment, inline=False, active_pattern_entries=None)"> | |
13 |
|
14 | |||
14 | <% |
|
15 | <% | |
15 |
|
|
16 | from rhodecode.model.comment import CommentsModel | |
16 |
|
|
17 | comment_model = CommentsModel() | |
|
18 | ||||
|
19 | comment_ver = comment.get_index_version(getattr(c, 'versions', [])) | |||
|
20 | latest_ver = len(getattr(c, 'versions', [])) | |||
|
21 | visible_for_user = True | |||
|
22 | if comment.draft: | |||
|
23 | visible_for_user = comment.user_id == c.rhodecode_user.user_id | |||
17 | %> |
|
24 | %> | |
18 | <% comment_ver = comment.get_index_version(getattr(c, 'versions', [])) %> |
|
|||
19 | <% latest_ver = len(getattr(c, 'versions', [])) %> |
|
|||
20 |
|
25 | |||
21 | % if inline: |
|
26 | % if inline: | |
22 | <% outdated_at_ver = comment.outdated_at_version(c.at_version_num) %> |
|
27 | <% outdated_at_ver = comment.outdated_at_version(c.at_version_num) %> | |
@@ -24,6 +29,7 b'' | |||||
24 | <% outdated_at_ver = comment.older_than_version(c.at_version_num) %> |
|
29 | <% outdated_at_ver = comment.older_than_version(c.at_version_num) %> | |
25 | % endif |
|
30 | % endif | |
26 |
|
31 | |||
|
32 | % if visible_for_user: | |||
27 | <div class="comment |
|
33 | <div class="comment | |
28 | ${'comment-inline' if inline else 'comment-general'} |
|
34 | ${'comment-inline' if inline else 'comment-general'} | |
29 | ${'comment-outdated' if outdated_at_ver else 'comment-current'}" |
|
35 | ${'comment-outdated' if outdated_at_ver else 'comment-current'}" | |
@@ -31,6 +37,7 b'' | |||||
31 | line="${comment.line_no}" |
|
37 | line="${comment.line_no}" | |
32 | data-comment-id="${comment.comment_id}" |
|
38 | data-comment-id="${comment.comment_id}" | |
33 | data-comment-type="${comment.comment_type}" |
|
39 | data-comment-type="${comment.comment_type}" | |
|
40 | data-comment-draft=${h.json.dumps(comment.draft)} | |||
34 | data-comment-renderer="${comment.renderer}" |
|
41 | data-comment-renderer="${comment.renderer}" | |
35 | data-comment-text="${comment.text | html_filters.base64,n}" |
|
42 | data-comment-text="${comment.text | html_filters.base64,n}" | |
36 | data-comment-line-no="${comment.line_no}" |
|
43 | data-comment-line-no="${comment.line_no}" | |
@@ -39,6 +46,9 b'' | |||||
39 |
|
46 | |||
40 | <div class="meta"> |
|
47 | <div class="meta"> | |
41 | <div class="comment-type-label"> |
|
48 | <div class="comment-type-label"> | |
|
49 | % if comment.draft: | |||
|
50 | <div class="tooltip comment-draft" title="${_('Draft comments are only visible to the author until submitted')}.">DRAFT</div> | |||
|
51 | % endif | |||
42 | <div class="comment-label ${comment.comment_type or 'note'}" id="comment-label-${comment.comment_id}"> |
|
52 | <div class="comment-label ${comment.comment_type or 'note'}" id="comment-label-${comment.comment_id}"> | |
43 |
|
53 | |||
44 | ## TODO COMMENT |
|
54 | ## TODO COMMENT | |
@@ -90,7 +100,7 b'' | |||||
90 |
|
100 | |||
91 | </div> |
|
101 | </div> | |
92 | </div> |
|
102 | </div> | |
93 |
|
103 | ## NOTE 0 and .. => because we disable it for now until UI ready | ||
94 | % if 0 and comment.status_change: |
|
104 | % if 0 and comment.status_change: | |
95 | <div class="pull-left"> |
|
105 | <div class="pull-left"> | |
96 | <span class="tag authortag tooltip" title="${_('Status from pull request.')}"> |
|
106 | <span class="tag authortag tooltip" title="${_('Status from pull request.')}"> | |
@@ -100,10 +110,12 b'' | |||||
100 | </span> |
|
110 | </span> | |
101 | </div> |
|
111 | </div> | |
102 | % endif |
|
112 | % endif | |
103 |
|
113 | ## Since only author can see drafts, we don't show it | ||
|
114 | % if not comment.draft: | |||
104 | <div class="author ${'author-inline' if inline else 'author-general'}"> |
|
115 | <div class="author ${'author-inline' if inline else 'author-general'}"> | |
105 | ${base.gravatar_with_user(comment.author.email, 16, tooltip=True)} |
|
116 | ${base.gravatar_with_user(comment.author.email, 16, tooltip=True)} | |
106 | </div> |
|
117 | </div> | |
|
118 | % endif | |||
107 |
|
119 | |||
108 | <div class="date"> |
|
120 | <div class="date"> | |
109 | ${h.age_component(comment.modified_at, time_is_local=True)} |
|
121 | ${h.age_component(comment.modified_at, time_is_local=True)} | |
@@ -215,6 +227,11 b'' | |||||
215 | <div class="dropdown-item"> |
|
227 | <div class="dropdown-item"> | |
216 | <a onclick="return Rhodecode.comments.deleteComment(this);" class="btn btn-link btn-sm btn-danger delete-comment">${_('Delete')}</a> |
|
228 | <a onclick="return Rhodecode.comments.deleteComment(this);" class="btn btn-link btn-sm btn-danger delete-comment">${_('Delete')}</a> | |
217 | </div> |
|
229 | </div> | |
|
230 | % if comment.draft: | |||
|
231 | <div class="dropdown-item"> | |||
|
232 | <a onclick="return Rhodecode.comments.finalizeDrafts([${comment.comment_id}]);" class="btn btn-link btn-sm finalize-draft-comment">${_('Submit draft')}</a> | |||
|
233 | </div> | |||
|
234 | % endif | |||
218 | %else: |
|
235 | %else: | |
219 | <div class="dropdown-divider"></div> |
|
236 | <div class="dropdown-divider"></div> | |
220 | <div class="dropdown-item"> |
|
237 | <div class="dropdown-item"> | |
@@ -252,6 +269,7 b'' | |||||
252 | </div> |
|
269 | </div> | |
253 |
|
270 | |||
254 | </div> |
|
271 | </div> | |
|
272 | % endif | |||
255 | </%def> |
|
273 | </%def> | |
256 |
|
274 | |||
257 | ## generate main comments |
|
275 | ## generate main comments | |
@@ -311,6 +329,7 b'' | |||||
311 | var text = self.cm.getValue(); |
|
329 | var text = self.cm.getValue(); | |
312 | var status = self.getCommentStatus(); |
|
330 | var status = self.getCommentStatus(); | |
313 | var commentType = self.getCommentType(); |
|
331 | var commentType = self.getCommentType(); | |
|
332 | var isDraft = self.getDraftState(); | |||
314 |
|
333 | |||
315 | if (text === "" && !status) { |
|
334 | if (text === "" && !status) { | |
316 | return; |
|
335 | return; | |
@@ -337,6 +356,7 b'' | |||||
337 | 'text': text, |
|
356 | 'text': text, | |
338 | 'changeset_status': status, |
|
357 | 'changeset_status': status, | |
339 | 'comment_type': commentType, |
|
358 | 'comment_type': commentType, | |
|
359 | 'draft': isDraft, | |||
340 | 'commit_ids': commitIds, |
|
360 | 'commit_ids': commitIds, | |
341 | 'csrf_token': CSRF_TOKEN |
|
361 | 'csrf_token': CSRF_TOKEN | |
342 | }; |
|
362 | }; | |
@@ -477,14 +497,18 b'' | |||||
477 | <div class="action-buttons-extra"></div> |
|
497 | <div class="action-buttons-extra"></div> | |
478 | % endif |
|
498 | % endif | |
479 |
|
499 | |||
480 |
<input class="btn btn-success comment-button-input" id="save_${lineno_id}" name="save" type="submit" value="${_(' |
|
500 | <input class="btn btn-success comment-button-input submit-comment-action" id="save_${lineno_id}" name="save" type="submit" value="${_('Add comment')}" data-is-draft=false onclick="$(this).addClass('submitter')"> | |
|
501 | ||||
|
502 | % if form_type == 'inline': | |||
|
503 | <input class="btn btn-warning comment-button-input submit-draft-action" id="save_draft_${lineno_id}" name="save_draft" type="submit" value="${_('Add draft')}" data-is-draft=true onclick="$(this).addClass('submitter')"> | |||
|
504 | % endif | |||
481 |
|
505 | |||
482 | ## inline for has a file, and line-number together with cancel hide button. |
|
506 | ## inline for has a file, and line-number together with cancel hide button. | |
483 | % if form_type == 'inline': |
|
507 | % if form_type == 'inline': | |
484 | <input type="hidden" name="f_path" value="{0}"> |
|
508 | <input type="hidden" name="f_path" value="{0}"> | |
485 | <input type="hidden" name="line" value="${lineno_id}"> |
|
509 | <input type="hidden" name="line" value="${lineno_id}"> | |
486 | <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);"> |
|
510 | <button type="button" class="tooltip cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);" title="Hide comment box"> | |
487 | ${_('Cancel')} |
|
511 | <i class="icon-cancel-circled2"></i> | |
488 | </button> |
|
512 | </button> | |
489 | % endif |
|
513 | % endif | |
490 | </div> |
|
514 | </div> |
@@ -711,16 +711,19 b' def get_comments_for(diff_type, comments' | |||||
711 | data-line-no="${line.original.lineno}" |
|
711 | data-line-no="${line.original.lineno}" | |
712 | > |
|
712 | > | |
713 |
|
713 | |||
714 | <% line_old_comments = None %> |
|
714 | <% line_old_comments, line_old_comments_no_drafts = None, None %> | |
715 | %if line.original.get_comment_args: |
|
715 | %if line.original.get_comment_args: | |
716 | <% line_old_comments = get_comments_for('side-by-side', inline_comments, *line.original.get_comment_args) %> |
|
716 | <% | |
|
717 | line_old_comments = get_comments_for('side-by-side', inline_comments, *line.original.get_comment_args) | |||
|
718 | line_old_comments_no_drafts = [c for c in line_old_comments if not c.draft] if line_old_comments else [] | |||
|
719 | has_outdated = any([x.outdated for x in line_old_comments_no_drafts]) | |||
|
720 | %> | |||
717 | %endif |
|
721 | %endif | |
718 | %if line_old_comments: |
|
722 | %if line_old_comments_no_drafts: | |
719 | <% has_outdated = any([x.outdated for x in line_old_comments]) %> |
|
|||
720 | % if has_outdated: |
|
723 | % if has_outdated: | |
721 | <i class="tooltip icon-comment-toggle" title="${_('comments including outdated: {}. Click here to display them.').format(len(line_old_comments))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> |
|
724 | <i class="tooltip icon-comment-toggle" title="${_('comments including outdated: {}. Click here to display them.').format(len(line_old_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> | |
722 | % else: |
|
725 | % else: | |
723 | <i class="tooltip icon-comment" title="${_('comments: {}. Click to toggle them.').format(len(line_old_comments))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> |
|
726 | <i class="tooltip icon-comment" title="${_('comments: {}. Click to toggle them.').format(len(line_old_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> | |
724 | % endif |
|
727 | % endif | |
725 | %endif |
|
728 | %endif | |
726 | </td> |
|
729 | </td> | |
@@ -752,18 +755,20 b' def get_comments_for(diff_type, comments' | |||||
752 | > |
|
755 | > | |
753 | <div> |
|
756 | <div> | |
754 |
|
|
757 | ||
|
758 | <% line_new_comments, line_new_comments_no_drafts = None, None %> | |||
755 | %if line.modified.get_comment_args: |
|
759 | %if line.modified.get_comment_args: | |
756 | <% line_new_comments = get_comments_for('side-by-side', inline_comments, *line.modified.get_comment_args) %> |
|
760 | <% | |
757 | %else: |
|
761 | line_new_comments = get_comments_for('side-by-side', inline_comments, *line.modified.get_comment_args) | |
758 | <% line_new_comments = None%> |
|
762 | line_new_comments_no_drafts = [c for c in line_new_comments if not c.draft] if line_new_comments else [] | |
|
763 | has_outdated = any([x.outdated for x in line_new_comments_no_drafts]) | |||
|
764 | %> | |||
759 | %endif |
|
765 | %endif | |
760 | %if line_new_comments: |
|
|||
761 |
|
766 | |||
762 | <% has_outdated = any([x.outdated for x in line_new_comments]) %> |
|
767 | %if line_new_comments_no_drafts: | |
763 | % if has_outdated: |
|
768 | % if has_outdated: | |
764 | <i class="tooltip icon-comment-toggle" title="${_('comments including outdated: {}. Click here to display them.').format(len(line_new_comments))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> |
|
769 | <i class="tooltip icon-comment-toggle" title="${_('comments including outdated: {}. Click here to display them.').format(len(line_new_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> | |
765 | % else: |
|
770 | % else: | |
766 | <i class="tooltip icon-comment" title="${_('comments: {}. Click to toggle them.').format(len(line_new_comments))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> |
|
771 | <i class="tooltip icon-comment" title="${_('comments: {}. Click to toggle them.').format(len(line_new_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> | |
767 | % endif |
|
772 | % endif | |
768 | %endif |
|
773 | %endif | |
769 | </div> |
|
774 | </div> | |
@@ -814,20 +819,22 b' def get_comments_for(diff_type, comments' | |||||
814 | <td class="cb-data ${action_class(action)}"> |
|
819 | <td class="cb-data ${action_class(action)}"> | |
815 | <div> |
|
820 | <div> | |
816 |
|
|
821 | ||
817 | %if comments_args: |
|
822 | <% comments, comments_no_drafts = None, None %> | |
818 | <% comments = get_comments_for('unified', inline_comments, *comments_args) %> |
|
823 | %if comments_args: | |
819 |
% |
|
824 | <% | |
820 | <% comments = None %> |
|
825 | comments = get_comments_for('unified', inline_comments, *comments_args) | |
821 | %endif |
|
826 | comments_no_drafts = [c for c in line_new_comments if not c.draft] if line_new_comments else [] | |
|
827 | has_outdated = any([x.outdated for x in comments_no_drafts]) | |||
|
828 | %> | |||
|
829 | %endif | |||
822 |
|
830 | |||
823 | % if comments: |
|
831 | % if comments_no_drafts: | |
824 | <% has_outdated = any([x.outdated for x in comments]) %> |
|
832 | % if has_outdated: | |
825 | % if has_outdated: |
|
833 | <i class="tooltip icon-comment-toggle" title="${_('comments including outdated: {}. Click here to display them.').format(len(comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> | |
826 | <i class="tooltip icon-comment-toggle" title="${_('comments including outdated: {}. Click here to display them.').format(len(comments))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> |
|
834 | % else: | |
827 | % else: |
|
835 | <i class="tooltip icon-comment" title="${_('comments: {}. Click to toggle them.').format(len(comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> | |
828 | <i class="tooltip icon-comment" title="${_('comments: {}. Click to toggle them.').format(len(comments))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> |
|
836 | % endif | |
829 | % endif |
|
837 | % endif | |
830 | % endif |
|
|||
831 | </div> |
|
838 | </div> | |
832 | </td> |
|
839 | </td> | |
833 | <td class="cb-lineno ${action_class(action)}" |
|
840 | <td class="cb-lineno ${action_class(action)}" |
@@ -980,9 +980,11 b' window.setObserversData = ${c.pull_reque' | |||||
980 | $(btns).each(fn_display); |
|
980 | $(btns).each(fn_display); | |
981 | }); |
|
981 | }); | |
982 |
|
982 | |||
983 | // register submit callback on commentForm form to track TODOs |
|
983 | // register submit callback on commentForm form to track TODOs, and refresh mergeChecks conditions | |
984 | window.commentFormGlobalSubmitSuccessCallback = function () { |
|
984 | window.commentFormGlobalSubmitSuccessCallback = function (comment) { | |
985 | refreshMergeChecks(); |
|
985 | if (!comment.draft) { | |
|
986 | refreshMergeChecks(); | |||
|
987 | } | |||
986 | }; |
|
988 | }; | |
987 |
|
989 | |||
988 | ReviewerAutoComplete('#user', reviewersController); |
|
990 | ReviewerAutoComplete('#user', reviewersController); | |
@@ -995,6 +997,12 b' window.setObserversData = ${c.pull_reque' | |||||
995 | var channel = '${c.pr_broadcast_channel}'; |
|
997 | var channel = '${c.pr_broadcast_channel}'; | |
996 | new ReviewerPresenceController(channel) |
|
998 | new ReviewerPresenceController(channel) | |
997 |
|
999 | |||
|
1000 | ||||
|
1001 | window.finalizeDrafts = function(commentIds) { | |||
|
1002 | alert('okok !' + commentIds) | |||
|
1003 | ||||
|
1004 | } | |||
|
1005 | ||||
998 | }) |
|
1006 | }) | |
999 | </script> |
|
1007 | </script> | |
1000 |
|
1008 |
General Comments 0
You need to be logged in to leave comments.
Login now