Show More
@@ -0,0 +1,30 b'' | |||||
|
1 | import logging | |||
|
2 | ||||
|
3 | from sqlalchemy import Column, MetaData, Integer, Unicode, ForeignKey | |||
|
4 | ||||
|
5 | from rhodecode.lib.dbmigrate.versions import _reset_base | |||
|
6 | ||||
|
7 | log = logging.getLogger(__name__) | |||
|
8 | ||||
|
9 | ||||
|
10 | def upgrade(migrate_engine): | |||
|
11 | """ | |||
|
12 | Upgrade operations go here. | |||
|
13 | Don't create your own engine; bind migrate_engine to your metadata | |||
|
14 | """ | |||
|
15 | _reset_base(migrate_engine) | |||
|
16 | from rhodecode.lib.dbmigrate.schema import db_4_5_0_0 as db | |||
|
17 | ||||
|
18 | # add comment type and link to resolve by id | |||
|
19 | comment_table = db.ChangesetComment.__table__ | |||
|
20 | col1 = Column('comment_type', Unicode(128), nullable=True) | |||
|
21 | col1.create(table=comment_table) | |||
|
22 | ||||
|
23 | col1 = Column('resolved_comment_id', Integer(), | |||
|
24 | ForeignKey('changeset_comments.comment_id'), nullable=True) | |||
|
25 | col1.create(table=comment_table) | |||
|
26 | ||||
|
27 | ||||
|
28 | def downgrade(migrate_engine): | |||
|
29 | meta = MetaData() | |||
|
30 | meta.bind = migrate_engine |
@@ -0,0 +1,70 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | ||||
|
3 | # Copyright (C) 2017-2017 RhodeCode GmbH | |||
|
4 | # | |||
|
5 | # This program is free software: you can redistribute it and/or modify | |||
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |||
|
7 | # (only), as published by the Free Software Foundation. | |||
|
8 | # | |||
|
9 | # This program is distributed in the hope that it will be useful, | |||
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
12 | # GNU General Public License for more details. | |||
|
13 | # | |||
|
14 | # You should have received a copy of the GNU Affero General Public License | |||
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
|
16 | # | |||
|
17 | # This program is dual-licensed. If you wish to learn more about the | |||
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |||
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |||
|
20 | ||||
|
21 | import os | |||
|
22 | ||||
|
23 | import colander | |||
|
24 | ||||
|
25 | from rhodecode.translation import _ | |||
|
26 | from rhodecode.model.validation_schema import preparers | |||
|
27 | from rhodecode.model.validation_schema import types | |||
|
28 | ||||
|
29 | ||||
|
30 | @colander.deferred | |||
|
31 | def deferred_lifetime_validator(node, kw): | |||
|
32 | options = kw.get('lifetime_options', []) | |||
|
33 | return colander.All( | |||
|
34 | colander.Range(min=-1, max=60 * 24 * 30 * 12), | |||
|
35 | colander.OneOf([x for x in options])) | |||
|
36 | ||||
|
37 | ||||
|
38 | def unique_gist_validator(node, value): | |||
|
39 | from rhodecode.model.db import Gist | |||
|
40 | existing = Gist.get_by_access_id(value) | |||
|
41 | if existing: | |||
|
42 | msg = _(u'Gist with name {} already exists').format(value) | |||
|
43 | raise colander.Invalid(node, msg) | |||
|
44 | ||||
|
45 | ||||
|
46 | def filename_validator(node, value): | |||
|
47 | if value != os.path.basename(value): | |||
|
48 | msg = _(u'Filename {} cannot be inside a directory').format(value) | |||
|
49 | raise colander.Invalid(node, msg) | |||
|
50 | ||||
|
51 | ||||
|
52 | comment_types = ['note', 'todo'] | |||
|
53 | ||||
|
54 | ||||
|
55 | class CommentSchema(colander.MappingSchema): | |||
|
56 | from rhodecode.model.db import ChangesetComment | |||
|
57 | ||||
|
58 | comment_body = colander.SchemaNode(colander.String()) | |||
|
59 | comment_type = colander.SchemaNode( | |||
|
60 | colander.String(), | |||
|
61 | validator=colander.OneOf(ChangesetComment.COMMENT_TYPES)) | |||
|
62 | ||||
|
63 | comment_file = colander.SchemaNode(colander.String(), missing=None) | |||
|
64 | comment_line = colander.SchemaNode(colander.String(), missing=None) | |||
|
65 | status_change = colander.SchemaNode(colander.String(), missing=None) | |||
|
66 | renderer_type = colander.SchemaNode(colander.String()) | |||
|
67 | ||||
|
68 | # do those ? | |||
|
69 | user = colander.SchemaNode(types.StrOrIntType()) | |||
|
70 | repo = colander.SchemaNode(types.StrOrIntType()) |
@@ -51,7 +51,7 b' PYRAMID_SETTINGS = {}' | |||||
51 | EXTENSIONS = {} |
|
51 | EXTENSIONS = {} | |
52 |
|
52 | |||
53 | __version__ = ('.'.join((str(each) for each in VERSION[:3]))) |
|
53 | __version__ = ('.'.join((str(each) for each in VERSION[:3]))) | |
54 |
__dbversion__ = 6 |
|
54 | __dbversion__ = 64 # defines current db version for migrations | |
55 | __platform__ = platform.system() |
|
55 | __platform__ = platform.system() | |
56 | __license__ = 'AGPLv3, and Commercial License' |
|
56 | __license__ = 'AGPLv3, and Commercial License' | |
57 | __author__ = 'RhodeCode GmbH' |
|
57 | __author__ = 'RhodeCode GmbH' |
@@ -334,6 +334,8 b' class ChangesetController(BaseRepoContro' | |||||
334 | commit_id = revision |
|
334 | commit_id = revision | |
335 | status = request.POST.get('changeset_status', None) |
|
335 | status = request.POST.get('changeset_status', None) | |
336 | text = request.POST.get('text') |
|
336 | text = request.POST.get('text') | |
|
337 | comment_type = request.POST.get('comment_type') | |||
|
338 | ||||
337 | if status: |
|
339 | if status: | |
338 | text = text or (_('Status change %(transition_icon)s %(status)s') |
|
340 | text = text or (_('Status change %(transition_icon)s %(status)s') | |
339 | % {'transition_icon': '>', |
|
341 | % {'transition_icon': '>', | |
@@ -355,7 +357,8 b' class ChangesetController(BaseRepoContro' | |||||
355 | line_no=request.POST.get('line'), |
|
357 | line_no=request.POST.get('line'), | |
356 | status_change=(ChangesetStatus.get_status_lbl(status) |
|
358 | status_change=(ChangesetStatus.get_status_lbl(status) | |
357 | if status else None), |
|
359 | if status else None), | |
358 | status_change_type=status |
|
360 | status_change_type=status, | |
|
361 | comment_type=comment_type | |||
359 | ) |
|
362 | ) | |
360 | c.inline_comment = True if comment.line_no else False |
|
363 | c.inline_comment = True if comment.line_no else False | |
361 |
|
364 |
@@ -903,6 +903,7 b' class PullrequestsController(BaseRepoCon' | |||||
903 | # as a changeset status, still we want to send it in one value. |
|
903 | # as a changeset status, still we want to send it in one value. | |
904 | status = request.POST.get('changeset_status', None) |
|
904 | status = request.POST.get('changeset_status', None) | |
905 | text = request.POST.get('text') |
|
905 | text = request.POST.get('text') | |
|
906 | comment_type = request.POST.get('comment_type') | |||
906 | if status and '_closed' in status: |
|
907 | if status and '_closed' in status: | |
907 | close_pr = True |
|
908 | close_pr = True | |
908 | status = status.replace('_closed', '') |
|
909 | status = status.replace('_closed', '') | |
@@ -934,7 +935,8 b' class PullrequestsController(BaseRepoCon' | |||||
934 | if status and allowed_to_change_status else None), |
|
935 | if status and allowed_to_change_status else None), | |
935 | status_change_type=(status |
|
936 | status_change_type=(status | |
936 | if status and allowed_to_change_status else None), |
|
937 | if status and allowed_to_change_status else None), | |
937 | closing_pr=close_pr |
|
938 | closing_pr=close_pr, | |
|
939 | comment_type=comment_type | |||
938 | ) |
|
940 | ) | |
939 |
|
941 | |||
940 | if allowed_to_change_status: |
|
942 | if allowed_to_change_status: |
@@ -56,7 +56,7 b' from rhodecode.lib.utils2 import (' | |||||
56 | str2bool, safe_unicode, AttributeDict, safe_int, md5, aslist) |
|
56 | str2bool, safe_unicode, AttributeDict, safe_int, md5, aslist) | |
57 | from rhodecode.lib.vcs.exceptions import RepositoryRequirementError |
|
57 | from rhodecode.lib.vcs.exceptions import RepositoryRequirementError | |
58 | from rhodecode.model import meta |
|
58 | from rhodecode.model import meta | |
59 | from rhodecode.model.db import Repository, User |
|
59 | from rhodecode.model.db import Repository, User, ChangesetComment | |
60 | from rhodecode.model.notification import NotificationModel |
|
60 | from rhodecode.model.notification import NotificationModel | |
61 | from rhodecode.model.scm import ScmModel |
|
61 | from rhodecode.model.scm import ScmModel | |
62 | from rhodecode.model.settings import VcsSettingsModel, SettingsModel |
|
62 | from rhodecode.model.settings import VcsSettingsModel, SettingsModel | |
@@ -299,6 +299,7 b' def attach_context_attributes(context, r' | |||||
299 | context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url') |
|
299 | context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url') | |
300 | context.visual.default_renderer = rc_config.get( |
|
300 | context.visual.default_renderer = rc_config.get( | |
301 | 'rhodecode_markup_renderer', 'rst') |
|
301 | 'rhodecode_markup_renderer', 'rst') | |
|
302 | context.visual.comment_types = ChangesetComment.COMMENT_TYPES | |||
302 | context.visual.rhodecode_support_url = \ |
|
303 | context.visual.rhodecode_support_url = \ | |
303 | rc_config.get('rhodecode_support_url') or url('rhodecode_support') |
|
304 | rc_config.get('rhodecode_support_url') or url('rhodecode_support') | |
304 |
|
305 |
@@ -44,6 +44,8 b' from rhodecode.model.notification import' | |||||
44 | from rhodecode.model.meta import Session |
|
44 | from rhodecode.model.meta import Session | |
45 | from rhodecode.model.settings import VcsSettingsModel |
|
45 | from rhodecode.model.settings import VcsSettingsModel | |
46 | from rhodecode.model.notification import EmailNotificationModel |
|
46 | from rhodecode.model.notification import EmailNotificationModel | |
|
47 | from rhodecode.model.validation_schema.schemas import comment_schema | |||
|
48 | ||||
47 |
|
49 | |||
48 | log = logging.getLogger(__name__) |
|
50 | log = logging.getLogger(__name__) | |
49 |
|
51 | |||
@@ -111,15 +113,32 b' class CommentsModel(BaseModel):' | |||||
111 | if not renderer: |
|
113 | if not renderer: | |
112 | renderer = self._get_renderer() |
|
114 | renderer = self._get_renderer() | |
113 |
|
115 | |||
114 | repo = self._get_repo(repo) |
|
116 | ||
115 | user = self._get_user(user) |
|
117 | schema = comment_schema.CommentSchema() | |
|
118 | validated_kwargs = schema.deserialize(dict( | |||
|
119 | comment_body=text, | |||
|
120 | comment_type=comment_type, | |||
|
121 | comment_file=f_path, | |||
|
122 | comment_line=line_no, | |||
|
123 | renderer_type=renderer, | |||
|
124 | status_change=status_change, | |||
|
125 | ||||
|
126 | repo=repo, | |||
|
127 | user=user, | |||
|
128 | )) | |||
|
129 | ||||
|
130 | repo = self._get_repo(validated_kwargs['repo']) | |||
|
131 | user = self._get_user(validated_kwargs['user']) | |||
|
132 | ||||
116 | comment = ChangesetComment() |
|
133 | comment = ChangesetComment() | |
117 | comment.renderer = renderer |
|
134 | comment.renderer = validated_kwargs['renderer_type'] | |
|
135 | comment.text = validated_kwargs['comment_body'] | |||
|
136 | comment.f_path = validated_kwargs['comment_file'] | |||
|
137 | comment.line_no = validated_kwargs['comment_line'] | |||
|
138 | comment.comment_type = validated_kwargs['comment_type'] | |||
|
139 | ||||
118 | comment.repo = repo |
|
140 | comment.repo = repo | |
119 | comment.author = user |
|
141 | comment.author = user | |
120 | comment.text = text |
|
|||
121 | comment.f_path = f_path |
|
|||
122 | comment.line_no = line_no |
|
|||
123 |
|
142 | |||
124 | pull_request_id = pull_request |
|
143 | pull_request_id = pull_request | |
125 |
|
144 |
@@ -2896,6 +2896,9 b' class ChangesetComment(Base, BaseModel):' | |||||
2896 | ) |
|
2896 | ) | |
2897 |
|
2897 | |||
2898 | COMMENT_OUTDATED = u'comment_outdated' |
|
2898 | COMMENT_OUTDATED = u'comment_outdated' | |
|
2899 | COMMENT_TYPE_NOTE = u'note' | |||
|
2900 | COMMENT_TYPE_TODO = u'todo' | |||
|
2901 | COMMENT_TYPES = [COMMENT_TYPE_NOTE, COMMENT_TYPE_TODO] | |||
2899 |
|
2902 | |||
2900 | comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True) |
|
2903 | comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True) | |
2901 | repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False) |
|
2904 | repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False) | |
@@ -2912,6 +2915,9 b' class ChangesetComment(Base, BaseModel):' | |||||
2912 | renderer = Column('renderer', Unicode(64), nullable=True) |
|
2915 | renderer = Column('renderer', Unicode(64), nullable=True) | |
2913 | display_state = Column('display_state', Unicode(128), nullable=True) |
|
2916 | display_state = Column('display_state', Unicode(128), nullable=True) | |
2914 |
|
2917 | |||
|
2918 | comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE) | |||
|
2919 | resolved_comment_id = Column('resolved_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=True) | |||
|
2920 | resolved_comment = relationship('ChangesetComment', remote_side=comment_id) | |||
2915 | author = relationship('User', lazy='joined') |
|
2921 | author = relationship('User', lazy='joined') | |
2916 | repo = relationship('Repository') |
|
2922 | repo = relationship('Repository') | |
2917 | status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan") |
|
2923 | status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan") |
@@ -186,3 +186,11 b' class UserType(UserOrUserGroupType):' | |||||
186 |
|
186 | |||
187 | class UserGroupType(UserOrUserGroupType): |
|
187 | class UserGroupType(UserOrUserGroupType): | |
188 | scopes = ('usergroup',) |
|
188 | scopes = ('usergroup',) | |
|
189 | ||||
|
190 | ||||
|
191 | class StrOrIntType(colander.String): | |||
|
192 | def deserialize(self, node, cstruct): | |||
|
193 | if isinstance(node, basestring): | |||
|
194 | return super(StrOrIntType, self).deserialize(node, cstruct) | |||
|
195 | else: | |||
|
196 | return colander.Integer().deserialize(node, cstruct) |
@@ -880,8 +880,6 b' input.filediff-collapse-state {' | |||||
880 | display: inline; |
|
880 | display: inline; | |
881 | } |
|
881 | } | |
882 |
|
882 | |||
883 | @comment-padding: 5px; |
|
|||
884 |
|
||||
885 | /**** COMMENTS ****/ |
|
883 | /**** COMMENTS ****/ | |
886 |
|
884 | |||
887 | .filediff-menu { |
|
885 | .filediff-menu { | |
@@ -909,59 +907,6 b' input.filediff-collapse-state {' | |||||
909 | } |
|
907 | } | |
910 | } |
|
908 | } | |
911 |
|
909 | |||
912 | .inline-comments { |
|
|||
913 | border-radius: @border-radius; |
|
|||
914 | .comment { |
|
|||
915 | margin: 0; |
|
|||
916 | border-radius: @border-radius; |
|
|||
917 | } |
|
|||
918 | .comment-outdated { |
|
|||
919 | opacity: 0.5; |
|
|||
920 | } |
|
|||
921 |
|
||||
922 | .comment-inline { |
|
|||
923 | background: white; |
|
|||
924 | padding: (@comment-padding + 3px) @comment-padding; |
|
|||
925 | border: @comment-padding solid @grey6; |
|
|||
926 |
|
||||
927 | .text { |
|
|||
928 | border: none; |
|
|||
929 | } |
|
|||
930 | .meta { |
|
|||
931 | border-bottom: 1px solid @grey6; |
|
|||
932 | padding-bottom: 10px; |
|
|||
933 | } |
|
|||
934 | } |
|
|||
935 | .comment-selected { |
|
|||
936 | border-left: 6px solid @comment-highlight-color; |
|
|||
937 | } |
|
|||
938 | .comment-inline-form { |
|
|||
939 | padding: @comment-padding; |
|
|||
940 | display: none; |
|
|||
941 | } |
|
|||
942 | .cb-comment-add-button { |
|
|||
943 | margin: @comment-padding; |
|
|||
944 | } |
|
|||
945 | /* hide add comment button when form is open */ |
|
|||
946 | .comment-inline-form-open ~ .cb-comment-add-button { |
|
|||
947 | display: none; |
|
|||
948 | } |
|
|||
949 | .comment-inline-form-open { |
|
|||
950 | display: block; |
|
|||
951 | } |
|
|||
952 | /* hide add comment button when form but no comments */ |
|
|||
953 | .comment-inline-form:first-child + .cb-comment-add-button { |
|
|||
954 | display: none; |
|
|||
955 | } |
|
|||
956 | /* hide add comment button when no comments or form */ |
|
|||
957 | .cb-comment-add-button:first-child { |
|
|||
958 | display: none; |
|
|||
959 | } |
|
|||
960 | /* hide add comment button when only comment is being deleted */ |
|
|||
961 | .comment-deleting:first-child + .cb-comment-add-button { |
|
|||
962 | display: none; |
|
|||
963 | } |
|
|||
964 | } |
|
|||
965 | /**** END COMMENTS ****/ |
|
910 | /**** END COMMENTS ****/ | |
966 |
|
911 | |||
967 | } |
|
912 | } |
@@ -47,6 +47,33 b' tr.inline-comments div {' | |||||
47 | visibility: hidden; |
|
47 | visibility: hidden; | |
48 | } |
|
48 | } | |
49 |
|
49 | |||
|
50 | .comment-label { | |||
|
51 | float: left; | |||
|
52 | ||||
|
53 | padding: 0.4em 0.4em; | |||
|
54 | margin: 2px 5px 0px -10px; | |||
|
55 | display: inline-block; | |||
|
56 | min-height: 0; | |||
|
57 | ||||
|
58 | text-align: center; | |||
|
59 | font-size: 10px; | |||
|
60 | line-height: .8em; | |||
|
61 | ||||
|
62 | font-family: @text-italic; | |||
|
63 | background: #fff none; | |||
|
64 | color: @grey4; | |||
|
65 | border: 1px solid @grey4; | |||
|
66 | white-space: nowrap; | |||
|
67 | ||||
|
68 | text-transform: uppercase; | |||
|
69 | ||||
|
70 | &.todo { | |||
|
71 | color: @color5; | |||
|
72 | font-family: @text-bold-italic; | |||
|
73 | } | |||
|
74 | } | |||
|
75 | ||||
|
76 | ||||
50 | .comment { |
|
77 | .comment { | |
51 |
|
78 | |||
52 | &.comment-general { |
|
79 | &.comment-general { | |
@@ -60,15 +87,19 b' tr.inline-comments div {' | |||||
60 |
|
87 | |||
61 | .rc-user { |
|
88 | .rc-user { | |
62 | min-width: 0; |
|
89 | min-width: 0; | |
63 |
margin: |
|
90 | margin: 0px .5em 0 0; | |
|
91 | ||||
|
92 | .user { | |||
|
93 | display: inline; | |||
|
94 | } | |||
64 | } |
|
95 | } | |
65 |
|
96 | |||
66 | .meta { |
|
97 | .meta { | |
67 | position: relative; |
|
98 | position: relative; | |
68 | width: 100%; |
|
99 | width: 100%; | |
69 | margin: 0 0 .5em 0; |
|
|||
70 | border-bottom: 1px solid @grey5; |
|
100 | border-bottom: 1px solid @grey5; | |
71 |
|
|
101 | margin: -5px 0px; | |
|
102 | line-height: 24px; | |||
72 |
|
103 | |||
73 | &:hover .permalink { |
|
104 | &:hover .permalink { | |
74 | visibility: visible; |
|
105 | visibility: visible; | |
@@ -87,10 +118,10 b' tr.inline-comments div {' | |||||
87 | } |
|
118 | } | |
88 |
|
119 | |||
89 | .author-general img { |
|
120 | .author-general img { | |
90 |
top: |
|
121 | top: 3px; | |
91 | } |
|
122 | } | |
92 | .author-inline img { |
|
123 | .author-inline img { | |
93 |
top: |
|
124 | top: 3px; | |
94 | } |
|
125 | } | |
95 |
|
126 | |||
96 | .status-change, |
|
127 | .status-change, | |
@@ -182,7 +213,7 b' tr.inline-comments div {' | |||||
182 | } |
|
213 | } | |
183 | .pr-version-inline { |
|
214 | .pr-version-inline { | |
184 | float: left; |
|
215 | float: left; | |
185 |
margin: |
|
216 | margin: 0px 4px; | |
186 | } |
|
217 | } | |
187 | .pr-version-num { |
|
218 | .pr-version-num { | |
188 | font-size: 10px; |
|
219 | font-size: 10px; | |
@@ -190,6 +221,64 b' tr.inline-comments div {' | |||||
190 |
|
221 | |||
191 | } |
|
222 | } | |
192 |
|
223 | |||
|
224 | @comment-padding: 5px; | |||
|
225 | ||||
|
226 | .inline-comments { | |||
|
227 | border-radius: @border-radius; | |||
|
228 | .comment { | |||
|
229 | margin: 0; | |||
|
230 | border-radius: @border-radius; | |||
|
231 | } | |||
|
232 | .comment-outdated { | |||
|
233 | opacity: 0.5; | |||
|
234 | } | |||
|
235 | ||||
|
236 | .comment-inline { | |||
|
237 | background: white; | |||
|
238 | padding: @comment-padding @comment-padding; | |||
|
239 | border: @comment-padding solid @grey6; | |||
|
240 | ||||
|
241 | .text { | |||
|
242 | border: none; | |||
|
243 | } | |||
|
244 | .meta { | |||
|
245 | border-bottom: 1px solid @grey6; | |||
|
246 | margin: -5px 0px; | |||
|
247 | line-height: 24px; | |||
|
248 | } | |||
|
249 | } | |||
|
250 | .comment-selected { | |||
|
251 | border-left: 6px solid @comment-highlight-color; | |||
|
252 | } | |||
|
253 | .comment-inline-form { | |||
|
254 | padding: @comment-padding; | |||
|
255 | display: none; | |||
|
256 | } | |||
|
257 | .cb-comment-add-button { | |||
|
258 | margin: @comment-padding; | |||
|
259 | } | |||
|
260 | /* hide add comment button when form is open */ | |||
|
261 | .comment-inline-form-open ~ .cb-comment-add-button { | |||
|
262 | display: none; | |||
|
263 | } | |||
|
264 | .comment-inline-form-open { | |||
|
265 | display: block; | |||
|
266 | } | |||
|
267 | /* hide add comment button when form but no comments */ | |||
|
268 | .comment-inline-form:first-child + .cb-comment-add-button { | |||
|
269 | display: none; | |||
|
270 | } | |||
|
271 | /* hide add comment button when no comments or form */ | |||
|
272 | .cb-comment-add-button:first-child { | |||
|
273 | display: none; | |||
|
274 | } | |||
|
275 | /* hide add comment button when only comment is being deleted */ | |||
|
276 | .comment-deleting:first-child + .cb-comment-add-button { | |||
|
277 | display: none; | |||
|
278 | } | |||
|
279 | } | |||
|
280 | ||||
|
281 | ||||
193 | .show-outdated-comments { |
|
282 | .show-outdated-comments { | |
194 | display: inline; |
|
283 | display: inline; | |
195 | color: @rcblue; |
|
284 | color: @rcblue; | |
@@ -300,6 +389,12 b' form.comment-form {' | |||||
300 | } |
|
389 | } | |
301 | } |
|
390 | } | |
302 |
|
391 | |||
|
392 | .comment-type { | |||
|
393 | margin: 0px; | |||
|
394 | border-radius: inherit; | |||
|
395 | border-color: @grey6; | |||
|
396 | } | |||
|
397 | ||||
303 | .preview-box { |
|
398 | .preview-box { | |
304 | min-height: 105px; |
|
399 | min-height: 105px; | |
305 | margin-bottom: 15px; |
|
400 | margin-bottom: 15px; |
@@ -688,6 +688,7 b' label {' | |||||
688 | padding: 0; |
|
688 | padding: 0; | |
689 | line-height: 1em; |
|
689 | line-height: 1em; | |
690 | border: 1px solid @grey4; |
|
690 | border: 1px solid @grey4; | |
|
691 | box-sizing: content-box; | |||
691 |
|
692 | |||
692 | &.gravatar-large { |
|
693 | &.gravatar-large { | |
693 | margin: -0.5em .25em -0.5em 0; |
|
694 | margin: -0.5em .25em -0.5em 0; |
@@ -132,12 +132,13 b' var CommentForm = (function() {' | |||||
132 |
|
132 | |||
133 | this.editButton = this.withLineNo('#edit-btn'); |
|
133 | this.editButton = this.withLineNo('#edit-btn'); | |
134 | this.editContainer = this.withLineNo('#edit-container'); |
|
134 | this.editContainer = this.withLineNo('#edit-container'); | |
|
135 | this.cancelButton = this.withLineNo('#cancel-btn'); | |||
|
136 | this.commentType = this.withLineNo('#comment_type'); | |||
135 |
|
137 | |||
136 |
this.c |
|
138 | this.cmBox = this.withLineNo('#text'); | |
|
139 | this.cm = initCommentBoxCodeMirror(this.cmBox, this.initAutocompleteActions); | |||
137 |
|
140 | |||
138 | this.statusChange = '#change_status'; |
|
141 | this.statusChange = '#change_status'; | |
139 | this.cmBox = this.withLineNo('#text'); |
|
|||
140 | this.cm = initCommentBoxCodeMirror(this.cmBox, this.initAutocompleteActions); |
|
|||
141 |
|
142 | |||
142 | this.submitForm = formElement; |
|
143 | this.submitForm = formElement; | |
143 | this.submitButton = $(this.submitForm).find('input[type="submit"]'); |
|
144 | this.submitButton = $(this.submitForm).find('input[type="submit"]'); | |
@@ -172,7 +173,9 b' var CommentForm = (function() {' | |||||
172 | this.getCommentStatus = function() { |
|
173 | this.getCommentStatus = function() { | |
173 | return $(this.submitForm).find(this.statusChange).val(); |
|
174 | return $(this.submitForm).find(this.statusChange).val(); | |
174 | }; |
|
175 | }; | |
175 |
|
176 | this.getCommentType = function() { | ||
|
177 | return $(this.submitForm).find(this.commentType).val(); | |||
|
178 | }; | |||
176 | this.isAllowedToSubmit = function() { |
|
179 | this.isAllowedToSubmit = function() { | |
177 | return !$(this.submitButton).prop('disabled'); |
|
180 | return !$(this.submitButton).prop('disabled'); | |
178 | }; |
|
181 | }; | |
@@ -256,6 +259,7 b' var CommentForm = (function() {' | |||||
256 | this.handleFormSubmit = function() { |
|
259 | this.handleFormSubmit = function() { | |
257 | var text = self.cm.getValue(); |
|
260 | var text = self.cm.getValue(); | |
258 | var status = self.getCommentStatus(); |
|
261 | var status = self.getCommentStatus(); | |
|
262 | var commentType = self.getCommentType(); | |||
259 |
|
263 | |||
260 | if (text === "" && !status) { |
|
264 | if (text === "" && !status) { | |
261 | return; |
|
265 | return; | |
@@ -268,6 +272,7 b' var CommentForm = (function() {' | |||||
268 | var postData = { |
|
272 | var postData = { | |
269 | 'text': text, |
|
273 | 'text': text, | |
270 | 'changeset_status': status, |
|
274 | 'changeset_status': status, | |
|
275 | 'comment_type': commentType, | |||
271 | 'csrf_token': CSRF_TOKEN |
|
276 | 'csrf_token': CSRF_TOKEN | |
272 | }; |
|
277 | }; | |
273 |
|
278 | |||
@@ -536,6 +541,7 b' var CommentsController = function() { /*' | |||||
536 | $filediff.removeClass('hide-comments'); |
|
541 | $filediff.removeClass('hide-comments'); | |
537 | var f_path = $filediff.attr('data-f-path'); |
|
542 | var f_path = $filediff.attr('data-f-path'); | |
538 | var lineno = self.getLineNumber(node); |
|
543 | var lineno = self.getLineNumber(node); | |
|
544 | ||||
539 | tmpl = tmpl.format(f_path, lineno); |
|
545 | tmpl = tmpl.format(f_path, lineno); | |
540 | $form = $(tmpl); |
|
546 | $form = $(tmpl); | |
541 |
|
547 | |||
@@ -557,6 +563,7 b' var CommentsController = function() { /*' | |||||
557 | // set a CUSTOM submit handler for inline comments. |
|
563 | // set a CUSTOM submit handler for inline comments. | |
558 | commentForm.setHandleFormSubmit(function(o) { |
|
564 | commentForm.setHandleFormSubmit(function(o) { | |
559 | var text = commentForm.cm.getValue(); |
|
565 | var text = commentForm.cm.getValue(); | |
|
566 | var commentType = commentForm.getCommentType(); | |||
560 |
|
567 | |||
561 | if (text === "") { |
|
568 | if (text === "") { | |
562 | return; |
|
569 | return; | |
@@ -579,6 +586,7 b' var CommentsController = function() { /*' | |||||
579 | 'text': text, |
|
586 | 'text': text, | |
580 | 'f_path': f_path, |
|
587 | 'f_path': f_path, | |
581 | 'line': lineno, |
|
588 | 'line': lineno, | |
|
589 | 'comment_type': commentType, | |||
582 | 'csrf_token': CSRF_TOKEN |
|
590 | 'csrf_token': CSRF_TOKEN | |
583 | }; |
|
591 | }; | |
584 | var submitSuccessCallback = function(json_data) { |
|
592 | var submitSuccessCallback = function(json_data) { |
@@ -18,8 +18,14 b'' | |||||
18 | style="${'display: none;' if outdated_at_ver else ''}"> |
|
18 | style="${'display: none;' if outdated_at_ver else ''}"> | |
19 |
|
19 | |||
20 | <div class="meta"> |
|
20 | <div class="meta"> | |
|
21 | <div class="comment-type-label tooltip"> | |||
|
22 | <div class="comment-label ${comment.comment_type or 'note'}"> | |||
|
23 | ${comment.comment_type or 'note'} | |||
|
24 | </div> | |||
|
25 | </div> | |||
|
26 | ||||
21 | <div class="author ${'author-inline' if inline else 'author-general'}"> |
|
27 | <div class="author ${'author-inline' if inline else 'author-general'}"> | |
22 |
${base.gravatar_with_user(comment.author.email, |
|
28 | ${base.gravatar_with_user(comment.author.email, 16)} | |
23 | </div> |
|
29 | </div> | |
24 | <div class="date"> |
|
30 | <div class="date"> | |
25 | ${h.age_component(comment.modified_at, time_is_local=True)} |
|
31 | ${h.age_component(comment.modified_at, time_is_local=True)} | |
@@ -31,16 +37,14 b'' | |||||
31 | % if comment.pull_request: |
|
37 | % if comment.pull_request: | |
32 | <a href="${h.url('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id)}"> |
|
38 | <a href="${h.url('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id)}"> | |
33 | % if comment.status_change: |
|
39 | % if comment.status_change: | |
34 |
${_(' |
|
40 | ${_('pull request #%s') % comment.pull_request.pull_request_id}: | |
35 | % else: |
|
41 | % else: | |
36 |
${_(' |
|
42 | ${_('pull request #%s') % comment.pull_request.pull_request_id} | |
37 | % endif |
|
43 | % endif | |
38 | </a> |
|
44 | </a> | |
39 | % else: |
|
45 | % else: | |
40 | % if comment.status_change: |
|
46 | % if comment.status_change: | |
41 | ${_('Status change on commit')}: |
|
47 | ${_('Status change on commit')}: | |
42 | % else: |
|
|||
43 | ${_('Comment on commit')} |
|
|||
44 | % endif |
|
48 | % endif | |
45 | % endif |
|
49 | % endif | |
46 | </div> |
|
50 | </div> | |
@@ -185,6 +189,13 b'' | |||||
185 | <li class=""> |
|
189 | <li class=""> | |
186 | <a href="#preview-btn" tabindex="-1" id="preview-btn">${_('Preview')}</a> |
|
190 | <a href="#preview-btn" tabindex="-1" id="preview-btn">${_('Preview')}</a> | |
187 | </li> |
|
191 | </li> | |
|
192 | <li class="pull-right"> | |||
|
193 | <select class="comment-type" id="comment_type" name="comment_type"> | |||
|
194 | % for val in c.visual.comment_types: | |||
|
195 | <option value="${val}">${val.upper()}</option> | |||
|
196 | % endfor | |||
|
197 | </select> | |||
|
198 | </li> | |||
188 | </ul> |
|
199 | </ul> | |
189 | </div> |
|
200 | </div> | |
190 |
|
201 |
@@ -70,6 +70,13 b" return h.url('', **new_args)" | |||||
70 | <li class=""> |
|
70 | <li class=""> | |
71 | <a href="#preview-btn" tabindex="-1" id="preview-btn_{1}">${_('Preview')}</a> |
|
71 | <a href="#preview-btn" tabindex="-1" id="preview-btn_{1}">${_('Preview')}</a> | |
72 | </li> |
|
72 | </li> | |
|
73 | <li class="pull-right"> | |||
|
74 | <select class="comment-type" id="comment_type_{1}" name="comment_type"> | |||
|
75 | % for val in c.visual.comment_types: | |||
|
76 | <option value="${val}">${val.upper()}</option> | |||
|
77 | % endfor | |||
|
78 | </select> | |||
|
79 | </li> | |||
73 | </ul> |
|
80 | </ul> | |
74 | </div> |
|
81 | </div> | |
75 |
|
82 |
General Comments 0
You need to be logged in to leave comments.
Login now