##// END OF EJS Templates
comments: added immutable parameter to forbid editing/deleting certain comments
marcink -
r4327:da58ea77 default
parent child Browse files
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 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__ = 105 # defines current db version for migrations
51 __dbversion__ = 106 # 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'
@@ -220,7 +220,7 b' def db():'
220 'pr_comment_url': 'http://comment-url',
220 'pr_comment_url': 'http://comment-url',
221 'pr_comment_reply_url': 'http://comment-url#reply',
221 'pr_comment_reply_url': 'http://comment-url#reply',
222
222
223 'comment_file': 'rhodecode/model/db.py',
223 'comment_file': 'rhodecode/model/get_flow_commits',
224 'comment_line': 'o1210',
224 'comment_line': 'o1210',
225 'comment_type': 'todo',
225 'comment_type': 'todo',
226 'comment_body': '''
226 'comment_body': '''
@@ -22,8 +22,7 b' import pytest'
22
22
23 from rhodecode.tests import TestController
23 from rhodecode.tests import TestController
24
24
25 from rhodecode.model.db import (
25 from rhodecode.model.db import ChangesetComment, Notification
26 ChangesetComment, Notification, UserNotification)
27 from rhodecode.model.meta import Session
26 from rhodecode.model.meta import Session
28 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
29
28
@@ -269,7 +268,36 b' class TestRepoCommitCommentsView(TestCon'
269 repo_name=backend.repo_name, commit_id=commit_id))
268 repo_name=backend.repo_name, commit_id=commit_id))
270 assert_comment_links(response, 0, 0)
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 ('rst', 'plain text', '<p>plain text</p>'),
301 ('rst', 'plain text', '<p>plain text</p>'),
274 ('rst', 'header\n======', '<h1 class="title">header</h1>'),
302 ('rst', 'header\n======', '<h1 class="title">header</h1>'),
275 ('rst', '*italics*', '<em>italics</em>'),
303 ('rst', '*italics*', '<em>italics</em>'),
@@ -280,11 +308,11 b' class TestRepoCommitCommentsView(TestCon'
280 ('markdown', '**bold**', '<strong>bold</strong>'),
308 ('markdown', '**bold**', '<strong>bold</strong>'),
281 ], ids=['rst-plain', 'rst-header', 'rst-italics', 'rst-bold', 'md-plain',
309 ], ids=['rst-plain', 'rst-header', 'rst-italics', 'rst-bold', 'md-plain',
282 'md-header', 'md-italics', 'md-bold', ])
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 self.log_user()
312 self.log_user()
285 params = {
313 params = {
286 'renderer': renderer,
314 'renderer': renderer,
287 'text': input,
315 'text': text_input,
288 'csrf_token': self.csrf_token
316 'csrf_token': self.csrf_token
289 }
317 }
290 commit_id = '0' * 16 # fake this for tests
318 commit_id = '0' * 16 # fake this for tests
@@ -22,7 +22,7 b''
22 import logging
22 import logging
23 import collections
23 import collections
24
24
25 from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound
25 from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound, HTTPForbidden
26 from pyramid.view import view_config
26 from pyramid.view import view_config
27 from pyramid.renderers import render
27 from pyramid.renderers import render
28 from pyramid.response import Response
28 from pyramid.response import Response
@@ -538,6 +538,10 b' class RepoCommitsView(RepoAppView):'
538 # comment already deleted in another call probably
538 # comment already deleted in another call probably
539 return True
539 return True
540
540
541 if comment.immutable:
542 # don't allow deleting comments that are immutable
543 raise HTTPForbidden()
544
541 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
545 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
542 super_admin = h.HasPermissionAny('hg.admin')()
546 super_admin = h.HasPermissionAny('hg.admin')()
543 comment_owner = (comment.author.user_id == self._rhodecode_db_user.user_id)
547 comment_owner = (comment.author.user_id == self._rhodecode_db_user.user_id)
@@ -1473,6 +1473,10 b' class RepoPullRequestsView(RepoAppView, '
1473 self.request.matchdict['comment_id'])
1473 self.request.matchdict['comment_id'])
1474 comment_id = comment.comment_id
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 if pull_request.is_closed():
1480 if pull_request.is_closed():
1477 log.debug('comment: forbidden because pull request is closed')
1481 log.debug('comment: forbidden because pull request is closed')
1478 raise HTTPForbidden()
1482 raise HTTPForbidden()
@@ -3711,6 +3711,9 b' class ChangesetComment(Base, BaseModel):'
3711 COMMENT_TYPE_TODO = u'todo'
3711 COMMENT_TYPE_TODO = u'todo'
3712 COMMENT_TYPES = [COMMENT_TYPE_NOTE, COMMENT_TYPE_TODO]
3712 COMMENT_TYPES = [COMMENT_TYPE_NOTE, COMMENT_TYPE_TODO]
3713
3713
3714 OP_IMMUTABLE = u'immutable'
3715 OP_CHANGEABLE = u'changeable'
3716
3714 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
3717 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
3715 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
3718 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
3716 revision = Column('revision', String(40), nullable=True)
3719 revision = Column('revision', String(40), nullable=True)
@@ -3725,6 +3728,7 b' class ChangesetComment(Base, BaseModel):'
3725 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3728 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3726 renderer = Column('renderer', Unicode(64), nullable=True)
3729 renderer = Column('renderer', Unicode(64), nullable=True)
3727 display_state = Column('display_state', Unicode(128), nullable=True)
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 comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE)
3733 comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE)
3730 resolved_comment_id = Column('resolved_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=True)
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 def outdated(self):
3771 def outdated(self):
3768 return self.display_state == self.COMMENT_OUTDATED
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 def outdated_at_version(self, version):
3778 def outdated_at_version(self, version):
3771 """
3779 """
3772 Checks if comment is outdated for given pull request version
3780 Checks if comment is outdated for given pull request version
@@ -135,7 +135,7 b''
135 ## only super-admin, repo admin OR comment owner can delete, also hide delete if currently viewed comment is outdated
135 ## only super-admin, repo admin OR comment owner can delete, also hide delete if currently viewed comment is outdated
136 %if not outdated_at_ver and (not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed())):
136 %if not outdated_at_ver and (not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed())):
137 ## permissions to delete
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 ## TODO: dan: add edit comment here
139 ## TODO: dan: add edit comment here
140 <a onclick="return Rhodecode.comments.deleteComment(this);" class="delete-comment"> ${_('Delete')}</a>
140 <a onclick="return Rhodecode.comments.deleteComment(this);" class="delete-comment"> ${_('Delete')}</a>
141 %else:
141 %else:
General Comments 0
You need to be logged in to leave comments. Login now