##// 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 48 EXTENSIONS = {}
49 49
50 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 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/db.py',
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