Show More
@@ -0,0 +1,43 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 | from sqlalchemy import BigInteger | |
|
9 | ||
|
10 | from rhodecode.lib.dbmigrate.versions import _reset_base | |
|
11 | from rhodecode.model import init_model_encryption | |
|
12 | ||
|
13 | ||
|
14 | log = logging.getLogger(__name__) | |
|
15 | ||
|
16 | ||
|
17 | def upgrade(migrate_engine): | |
|
18 | """ | |
|
19 | Upgrade operations go here. | |
|
20 | Don't create your own engine; bind migrate_engine to your metadata | |
|
21 | """ | |
|
22 | _reset_base(migrate_engine) | |
|
23 | from rhodecode.lib.dbmigrate.schema import db_4_18_0_1 as db | |
|
24 | ||
|
25 | init_model_encryption(db) | |
|
26 | ||
|
27 | context = MigrationContext.configure(migrate_engine.connect()) | |
|
28 | op = Operations(context) | |
|
29 | ||
|
30 | comments = db.ChangesetComment.__table__ | |
|
31 | ||
|
32 | with op.batch_alter_table(comments.name) as batch_op: | |
|
33 | new_column = Column('immutable_state', Unicode(128), nullable=True) | |
|
34 | batch_op.add_column(new_column) | |
|
35 | ||
|
36 | ||
|
37 | def downgrade(migrate_engine): | |
|
38 | meta = MetaData() | |
|
39 | meta.bind = migrate_engine | |
|
40 | ||
|
41 | ||
|
42 | def fixups(models, _SESSION): | |
|
43 | pass |
@@ -48,7 +48,7 b' PYRAMID_SETTINGS = {}' | |||
|
48 | 48 | EXTENSIONS = {} |
|
49 | 49 | |
|
50 | 50 | __version__ = ('.'.join((str(each) for each in VERSION[:3]))) |
|
51 |
__dbversion__ = 10 |
|
|
51 | __dbversion__ = 106 # defines current db version for migrations | |
|
52 | 52 | __platform__ = platform.system() |
|
53 | 53 | __license__ = 'AGPLv3, and Commercial License' |
|
54 | 54 | __author__ = 'RhodeCode GmbH' |
@@ -220,7 +220,7 b' def db():' | |||
|
220 | 220 | 'pr_comment_url': 'http://comment-url', |
|
221 | 221 | 'pr_comment_reply_url': 'http://comment-url#reply', |
|
222 | 222 | |
|
223 |
'comment_file': 'rhodecode/model/ |
|
|
223 | 'comment_file': 'rhodecode/model/get_flow_commits', | |
|
224 | 224 | 'comment_line': 'o1210', |
|
225 | 225 | 'comment_type': 'todo', |
|
226 | 226 | 'comment_body': ''' |
@@ -22,8 +22,7 b' import pytest' | |||
|
22 | 22 | |
|
23 | 23 | from rhodecode.tests import TestController |
|
24 | 24 | |
|
25 |
from rhodecode.model.db import |
|
|
26 | ChangesetComment, Notification, UserNotification) | |
|
25 | from rhodecode.model.db import ChangesetComment, Notification | |
|
27 | 26 | from rhodecode.model.meta import Session |
|
28 | 27 | from rhodecode.lib import helpers as h |
|
29 | 28 | |
@@ -269,7 +268,36 b' class TestRepoCommitCommentsView(TestCon' | |||
|
269 | 268 | repo_name=backend.repo_name, commit_id=commit_id)) |
|
270 | 269 | assert_comment_links(response, 0, 0) |
|
271 | 270 | |
|
272 | @pytest.mark.parametrize('renderer, input, output', [ | |
|
271 | def test_delete_forbidden_for_immutable_comments(self, backend): | |
|
272 | self.log_user() | |
|
273 | commit_id = backend.repo.get_commit('300').raw_id | |
|
274 | text = u'CommentOnCommit' | |
|
275 | ||
|
276 | params = {'text': text, 'csrf_token': self.csrf_token} | |
|
277 | self.app.post( | |
|
278 | route_path( | |
|
279 | 'repo_commit_comment_create', | |
|
280 | repo_name=backend.repo_name, commit_id=commit_id), | |
|
281 | params=params) | |
|
282 | ||
|
283 | comments = ChangesetComment.query().all() | |
|
284 | assert len(comments) == 1 | |
|
285 | comment_id = comments[0].comment_id | |
|
286 | ||
|
287 | comment = ChangesetComment.get(comment_id) | |
|
288 | comment.immutable_state = ChangesetComment.OP_IMMUTABLE | |
|
289 | Session().add(comment) | |
|
290 | Session().commit() | |
|
291 | ||
|
292 | self.app.post( | |
|
293 | route_path('repo_commit_comment_delete', | |
|
294 | repo_name=backend.repo_name, | |
|
295 | commit_id=commit_id, | |
|
296 | comment_id=comment_id), | |
|
297 | params={'csrf_token': self.csrf_token}, | |
|
298 | status=403) | |
|
299 | ||
|
300 | @pytest.mark.parametrize('renderer, text_input, output', [ | |
|
273 | 301 | ('rst', 'plain text', '<p>plain text</p>'), |
|
274 | 302 | ('rst', 'header\n======', '<h1 class="title">header</h1>'), |
|
275 | 303 | ('rst', '*italics*', '<em>italics</em>'), |
@@ -280,11 +308,11 b' class TestRepoCommitCommentsView(TestCon' | |||
|
280 | 308 | ('markdown', '**bold**', '<strong>bold</strong>'), |
|
281 | 309 | ], ids=['rst-plain', 'rst-header', 'rst-italics', 'rst-bold', 'md-plain', |
|
282 | 310 | 'md-header', 'md-italics', 'md-bold', ]) |
|
283 | def test_preview(self, renderer, input, output, backend, xhr_header): | |
|
311 | def test_preview(self, renderer, text_input, output, backend, xhr_header): | |
|
284 | 312 | self.log_user() |
|
285 | 313 | params = { |
|
286 | 314 | 'renderer': renderer, |
|
287 | 'text': input, | |
|
315 | 'text': text_input, | |
|
288 | 316 | 'csrf_token': self.csrf_token |
|
289 | 317 | } |
|
290 | 318 | commit_id = '0' * 16 # fake this for tests |
@@ -22,7 +22,7 b'' | |||
|
22 | 22 | import logging |
|
23 | 23 | import collections |
|
24 | 24 | |
|
25 | from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound | |
|
25 | from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound, HTTPForbidden | |
|
26 | 26 | from pyramid.view import view_config |
|
27 | 27 | from pyramid.renderers import render |
|
28 | 28 | from pyramid.response import Response |
@@ -538,6 +538,10 b' class RepoCommitsView(RepoAppView):' | |||
|
538 | 538 | # comment already deleted in another call probably |
|
539 | 539 | return True |
|
540 | 540 | |
|
541 | if comment.immutable: | |
|
542 | # don't allow deleting comments that are immutable | |
|
543 | raise HTTPForbidden() | |
|
544 | ||
|
541 | 545 | is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name) |
|
542 | 546 | super_admin = h.HasPermissionAny('hg.admin')() |
|
543 | 547 | comment_owner = (comment.author.user_id == self._rhodecode_db_user.user_id) |
@@ -1473,6 +1473,10 b' class RepoPullRequestsView(RepoAppView, ' | |||
|
1473 | 1473 | self.request.matchdict['comment_id']) |
|
1474 | 1474 | comment_id = comment.comment_id |
|
1475 | 1475 | |
|
1476 | if comment.immutable: | |
|
1477 | # don't allow deleting comments that are immutable | |
|
1478 | raise HTTPForbidden() | |
|
1479 | ||
|
1476 | 1480 | if pull_request.is_closed(): |
|
1477 | 1481 | log.debug('comment: forbidden because pull request is closed') |
|
1478 | 1482 | raise HTTPForbidden() |
@@ -3711,6 +3711,9 b' class ChangesetComment(Base, BaseModel):' | |||
|
3711 | 3711 | COMMENT_TYPE_TODO = u'todo' |
|
3712 | 3712 | COMMENT_TYPES = [COMMENT_TYPE_NOTE, COMMENT_TYPE_TODO] |
|
3713 | 3713 | |
|
3714 | OP_IMMUTABLE = u'immutable' | |
|
3715 | OP_CHANGEABLE = u'changeable' | |
|
3716 | ||
|
3714 | 3717 | comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True) |
|
3715 | 3718 | repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False) |
|
3716 | 3719 | revision = Column('revision', String(40), nullable=True) |
@@ -3725,6 +3728,7 b' class ChangesetComment(Base, BaseModel):' | |||
|
3725 | 3728 | modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) |
|
3726 | 3729 | renderer = Column('renderer', Unicode(64), nullable=True) |
|
3727 | 3730 | display_state = Column('display_state', Unicode(128), nullable=True) |
|
3731 | immutable_state = Column('immutable_state', Unicode(128), nullable=True, default=OP_CHANGEABLE) | |
|
3728 | 3732 | |
|
3729 | 3733 | comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE) |
|
3730 | 3734 | resolved_comment_id = Column('resolved_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=True) |
@@ -3767,6 +3771,10 b' class ChangesetComment(Base, BaseModel):' | |||
|
3767 | 3771 | def outdated(self): |
|
3768 | 3772 | return self.display_state == self.COMMENT_OUTDATED |
|
3769 | 3773 | |
|
3774 | @property | |
|
3775 | def immutable(self): | |
|
3776 | return self.immutable_state == self.OP_IMMUTABLE | |
|
3777 | ||
|
3770 | 3778 | def outdated_at_version(self, version): |
|
3771 | 3779 | """ |
|
3772 | 3780 | Checks if comment is outdated for given pull request version |
@@ -135,7 +135,7 b'' | |||
|
135 | 135 | ## only super-admin, repo admin OR comment owner can delete, also hide delete if currently viewed comment is outdated |
|
136 | 136 | %if not outdated_at_ver and (not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed())): |
|
137 | 137 | ## permissions to delete |
|
138 | %if c.is_super_admin or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id: | |
|
138 | %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): | |
|
139 | 139 | ## TODO: dan: add edit comment here |
|
140 | 140 | <a onclick="return Rhodecode.comments.deleteComment(this);" class="delete-comment"> ${_('Delete')}</a> |
|
141 | 141 | %else: |
General Comments 0
You need to be logged in to leave comments.
Login now