##// END OF EJS Templates
pull-requests: allow markup rendered description.
marcink -
r2816:b1852ba4 default
parent child Browse files
Show More
@@ -1,49 +1,53 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 from rhodecode.config import routing_links
20 from rhodecode.config import routing_links
21
21
22
22
23 def includeme(config):
23 def includeme(config):
24
24
25 config.add_route(
25 config.add_route(
26 name='home',
26 name='home',
27 pattern='/')
27 pattern='/')
28
28
29 config.add_route(
29 config.add_route(
30 name='user_autocomplete_data',
30 name='user_autocomplete_data',
31 pattern='/_users')
31 pattern='/_users')
32
32
33 config.add_route(
33 config.add_route(
34 name='user_group_autocomplete_data',
34 name='user_group_autocomplete_data',
35 pattern='/_user_groups')
35 pattern='/_user_groups')
36
36
37 config.add_route(
37 config.add_route(
38 name='repo_list_data',
38 name='repo_list_data',
39 pattern='/_repos')
39 pattern='/_repos')
40
40
41 config.add_route(
41 config.add_route(
42 name='goto_switcher_data',
42 name='goto_switcher_data',
43 pattern='/_goto_data')
43 pattern='/_goto_data')
44
44
45 config.add_route(
46 name='markup_preview',
47 pattern='/_markup_preview')
48
45 # register our static links via redirection mechanism
49 # register our static links via redirection mechanism
46 routing_links.connect_redirection_links(config)
50 routing_links.connect_redirection_links(config)
47
51
48 # Scan module for configuration decorators.
52 # Scan module for configuration decorators.
49 config.scan('.views', ignore='.tests')
53 config.scan('.views', ignore='.tests')
@@ -1,427 +1,446 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import re
21 import re
22 import logging
22 import logging
23 import collections
23 import collections
24
24
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26
26
27 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.lib import helpers as h
28 from rhodecode.lib import helpers as h
29 from rhodecode.lib.auth import (
29 from rhodecode.lib.auth import (
30 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator)
30 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator,
31 CSRFRequired)
31 from rhodecode.lib.index import searcher_from_config
32 from rhodecode.lib.index import searcher_from_config
32 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
33 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
33 from rhodecode.lib.ext_json import json
34 from rhodecode.lib.ext_json import json
34 from rhodecode.model.db import (
35 from rhodecode.model.db import (
35 func, or_, in_filter_generator, Repository, RepoGroup, User, UserGroup)
36 func, or_, in_filter_generator, Repository, RepoGroup, User, UserGroup)
36 from rhodecode.model.repo import RepoModel
37 from rhodecode.model.repo import RepoModel
37 from rhodecode.model.repo_group import RepoGroupModel
38 from rhodecode.model.repo_group import RepoGroupModel
38 from rhodecode.model.scm import RepoGroupList, RepoList
39 from rhodecode.model.scm import RepoGroupList, RepoList
39 from rhodecode.model.user import UserModel
40 from rhodecode.model.user import UserModel
40 from rhodecode.model.user_group import UserGroupModel
41 from rhodecode.model.user_group import UserGroupModel
41
42
42 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
43
44
44
45
45 class HomeView(BaseAppView):
46 class HomeView(BaseAppView):
46
47
47 def load_default_context(self):
48 def load_default_context(self):
48 c = self._get_local_tmpl_context()
49 c = self._get_local_tmpl_context()
49 c.user = c.auth_user.get_instance()
50 c.user = c.auth_user.get_instance()
50
51
51 return c
52 return c
52
53
53 @LoginRequired()
54 @LoginRequired()
54 @view_config(
55 @view_config(
55 route_name='user_autocomplete_data', request_method='GET',
56 route_name='user_autocomplete_data', request_method='GET',
56 renderer='json_ext', xhr=True)
57 renderer='json_ext', xhr=True)
57 def user_autocomplete_data(self):
58 def user_autocomplete_data(self):
58 self.load_default_context()
59 self.load_default_context()
59 query = self.request.GET.get('query')
60 query = self.request.GET.get('query')
60 active = str2bool(self.request.GET.get('active') or True)
61 active = str2bool(self.request.GET.get('active') or True)
61 include_groups = str2bool(self.request.GET.get('user_groups'))
62 include_groups = str2bool(self.request.GET.get('user_groups'))
62 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
63 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
63 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
64 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
64
65
65 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
66 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
66 query, active, include_groups)
67 query, active, include_groups)
67
68
68 _users = UserModel().get_users(
69 _users = UserModel().get_users(
69 name_contains=query, only_active=active)
70 name_contains=query, only_active=active)
70
71
71 def maybe_skip_default_user(usr):
72 def maybe_skip_default_user(usr):
72 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
73 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
73 return False
74 return False
74 return True
75 return True
75 _users = filter(maybe_skip_default_user, _users)
76 _users = filter(maybe_skip_default_user, _users)
76
77
77 if include_groups:
78 if include_groups:
78 # extend with user groups
79 # extend with user groups
79 _user_groups = UserGroupModel().get_user_groups(
80 _user_groups = UserGroupModel().get_user_groups(
80 name_contains=query, only_active=active,
81 name_contains=query, only_active=active,
81 expand_groups=expand_groups)
82 expand_groups=expand_groups)
82 _users = _users + _user_groups
83 _users = _users + _user_groups
83
84
84 return {'suggestions': _users}
85 return {'suggestions': _users}
85
86
86 @LoginRequired()
87 @LoginRequired()
87 @NotAnonymous()
88 @NotAnonymous()
88 @view_config(
89 @view_config(
89 route_name='user_group_autocomplete_data', request_method='GET',
90 route_name='user_group_autocomplete_data', request_method='GET',
90 renderer='json_ext', xhr=True)
91 renderer='json_ext', xhr=True)
91 def user_group_autocomplete_data(self):
92 def user_group_autocomplete_data(self):
92 self.load_default_context()
93 self.load_default_context()
93 query = self.request.GET.get('query')
94 query = self.request.GET.get('query')
94 active = str2bool(self.request.GET.get('active') or True)
95 active = str2bool(self.request.GET.get('active') or True)
95 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
96 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
96
97
97 log.debug('generating user group list, query:%s, active:%s',
98 log.debug('generating user group list, query:%s, active:%s',
98 query, active)
99 query, active)
99
100
100 _user_groups = UserGroupModel().get_user_groups(
101 _user_groups = UserGroupModel().get_user_groups(
101 name_contains=query, only_active=active,
102 name_contains=query, only_active=active,
102 expand_groups=expand_groups)
103 expand_groups=expand_groups)
103 _user_groups = _user_groups
104 _user_groups = _user_groups
104
105
105 return {'suggestions': _user_groups}
106 return {'suggestions': _user_groups}
106
107
107 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
108 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
108 org_query = name_contains
109 org_query = name_contains
109 allowed_ids = self._rhodecode_user.repo_acl_ids(
110 allowed_ids = self._rhodecode_user.repo_acl_ids(
110 ['repository.read', 'repository.write', 'repository.admin'],
111 ['repository.read', 'repository.write', 'repository.admin'],
111 cache=False, name_filter=name_contains) or [-1]
112 cache=False, name_filter=name_contains) or [-1]
112
113
113 query = Repository.query()\
114 query = Repository.query()\
114 .order_by(func.length(Repository.repo_name))\
115 .order_by(func.length(Repository.repo_name))\
115 .order_by(Repository.repo_name)\
116 .order_by(Repository.repo_name)\
116 .filter(or_(
117 .filter(or_(
117 # generate multiple IN to fix limitation problems
118 # generate multiple IN to fix limitation problems
118 *in_filter_generator(Repository.repo_id, allowed_ids)
119 *in_filter_generator(Repository.repo_id, allowed_ids)
119 ))
120 ))
120
121
121 if repo_type:
122 if repo_type:
122 query = query.filter(Repository.repo_type == repo_type)
123 query = query.filter(Repository.repo_type == repo_type)
123
124
124 if name_contains:
125 if name_contains:
125 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
126 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
126 query = query.filter(
127 query = query.filter(
127 Repository.repo_name.ilike(ilike_expression))
128 Repository.repo_name.ilike(ilike_expression))
128 query = query.limit(limit)
129 query = query.limit(limit)
129
130
130 acl_iter = query
131 acl_iter = query
131
132
132 return [
133 return [
133 {
134 {
134 'id': obj.repo_name,
135 'id': obj.repo_name,
135 'value': org_query,
136 'value': org_query,
136 'value_display': obj.repo_name,
137 'value_display': obj.repo_name,
137 'text': obj.repo_name,
138 'text': obj.repo_name,
138 'type': 'repo',
139 'type': 'repo',
139 'repo_id': obj.repo_id,
140 'repo_id': obj.repo_id,
140 'repo_type': obj.repo_type,
141 'repo_type': obj.repo_type,
141 'private': obj.private,
142 'private': obj.private,
142 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
143 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
143 }
144 }
144 for obj in acl_iter]
145 for obj in acl_iter]
145
146
146 def _get_repo_group_list(self, name_contains=None, limit=20):
147 def _get_repo_group_list(self, name_contains=None, limit=20):
147 org_query = name_contains
148 org_query = name_contains
148 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
149 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
149 ['group.read', 'group.write', 'group.admin'],
150 ['group.read', 'group.write', 'group.admin'],
150 cache=False, name_filter=name_contains) or [-1]
151 cache=False, name_filter=name_contains) or [-1]
151
152
152 query = RepoGroup.query()\
153 query = RepoGroup.query()\
153 .order_by(func.length(RepoGroup.group_name))\
154 .order_by(func.length(RepoGroup.group_name))\
154 .order_by(RepoGroup.group_name) \
155 .order_by(RepoGroup.group_name) \
155 .filter(or_(
156 .filter(or_(
156 # generate multiple IN to fix limitation problems
157 # generate multiple IN to fix limitation problems
157 *in_filter_generator(RepoGroup.group_id, allowed_ids)
158 *in_filter_generator(RepoGroup.group_id, allowed_ids)
158 ))
159 ))
159
160
160 if name_contains:
161 if name_contains:
161 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
162 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
162 query = query.filter(
163 query = query.filter(
163 RepoGroup.group_name.ilike(ilike_expression))
164 RepoGroup.group_name.ilike(ilike_expression))
164 query = query.limit(limit)
165 query = query.limit(limit)
165
166
166 acl_iter = query
167 acl_iter = query
167
168
168 return [
169 return [
169 {
170 {
170 'id': obj.group_name,
171 'id': obj.group_name,
171 'value': org_query,
172 'value': org_query,
172 'value_display': obj.group_name,
173 'value_display': obj.group_name,
173 'type': 'repo_group',
174 'type': 'repo_group',
174 'url': h.route_path(
175 'url': h.route_path(
175 'repo_group_home', repo_group_name=obj.group_name)
176 'repo_group_home', repo_group_name=obj.group_name)
176 }
177 }
177 for obj in acl_iter]
178 for obj in acl_iter]
178
179
179 def _get_user_list(self, name_contains=None, limit=20):
180 def _get_user_list(self, name_contains=None, limit=20):
180 org_query = name_contains
181 org_query = name_contains
181 if not name_contains:
182 if not name_contains:
182 return []
183 return []
183
184
184 name_contains = re.compile('(?:user:)(.+)').findall(name_contains)
185 name_contains = re.compile('(?:user:)(.+)').findall(name_contains)
185 if len(name_contains) != 1:
186 if len(name_contains) != 1:
186 return []
187 return []
187 name_contains = name_contains[0]
188 name_contains = name_contains[0]
188
189
189 query = User.query()\
190 query = User.query()\
190 .order_by(func.length(User.username))\
191 .order_by(func.length(User.username))\
191 .order_by(User.username) \
192 .order_by(User.username) \
192 .filter(User.username != User.DEFAULT_USER)
193 .filter(User.username != User.DEFAULT_USER)
193
194
194 if name_contains:
195 if name_contains:
195 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
196 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
196 query = query.filter(
197 query = query.filter(
197 User.username.ilike(ilike_expression))
198 User.username.ilike(ilike_expression))
198 query = query.limit(limit)
199 query = query.limit(limit)
199
200
200 acl_iter = query
201 acl_iter = query
201
202
202 return [
203 return [
203 {
204 {
204 'id': obj.user_id,
205 'id': obj.user_id,
205 'value': org_query,
206 'value': org_query,
206 'value_display': obj.username,
207 'value_display': obj.username,
207 'type': 'user',
208 'type': 'user',
208 'icon_link': h.gravatar_url(obj.email, 30),
209 'icon_link': h.gravatar_url(obj.email, 30),
209 'url': h.route_path(
210 'url': h.route_path(
210 'user_profile', username=obj.username)
211 'user_profile', username=obj.username)
211 }
212 }
212 for obj in acl_iter]
213 for obj in acl_iter]
213
214
214 def _get_user_groups_list(self, name_contains=None, limit=20):
215 def _get_user_groups_list(self, name_contains=None, limit=20):
215 org_query = name_contains
216 org_query = name_contains
216 if not name_contains:
217 if not name_contains:
217 return []
218 return []
218
219
219 name_contains = re.compile('(?:user_group:)(.+)').findall(name_contains)
220 name_contains = re.compile('(?:user_group:)(.+)').findall(name_contains)
220 if len(name_contains) != 1:
221 if len(name_contains) != 1:
221 return []
222 return []
222 name_contains = name_contains[0]
223 name_contains = name_contains[0]
223
224
224 query = UserGroup.query()\
225 query = UserGroup.query()\
225 .order_by(func.length(UserGroup.users_group_name))\
226 .order_by(func.length(UserGroup.users_group_name))\
226 .order_by(UserGroup.users_group_name)
227 .order_by(UserGroup.users_group_name)
227
228
228 if name_contains:
229 if name_contains:
229 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
230 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
230 query = query.filter(
231 query = query.filter(
231 UserGroup.users_group_name.ilike(ilike_expression))
232 UserGroup.users_group_name.ilike(ilike_expression))
232 query = query.limit(limit)
233 query = query.limit(limit)
233
234
234 acl_iter = query
235 acl_iter = query
235
236
236 return [
237 return [
237 {
238 {
238 'id': obj.users_group_id,
239 'id': obj.users_group_id,
239 'value': org_query,
240 'value': org_query,
240 'value_display': obj.users_group_name,
241 'value_display': obj.users_group_name,
241 'type': 'user_group',
242 'type': 'user_group',
242 'url': h.route_path(
243 'url': h.route_path(
243 'user_group_profile', user_group_name=obj.users_group_name)
244 'user_group_profile', user_group_name=obj.users_group_name)
244 }
245 }
245 for obj in acl_iter]
246 for obj in acl_iter]
246
247
247 def _get_hash_commit_list(self, auth_user, query):
248 def _get_hash_commit_list(self, auth_user, query):
248 org_query = query
249 org_query = query
249 if not query or len(query) < 3:
250 if not query or len(query) < 3:
250 return []
251 return []
251
252
252 commit_hashes = re.compile('(?:commit:)([0-9a-f]{2,40})').findall(query)
253 commit_hashes = re.compile('(?:commit:)([0-9a-f]{2,40})').findall(query)
253
254
254 if len(commit_hashes) != 1:
255 if len(commit_hashes) != 1:
255 return []
256 return []
256 commit_hash = commit_hashes[0]
257 commit_hash = commit_hashes[0]
257
258
258 searcher = searcher_from_config(self.request.registry.settings)
259 searcher = searcher_from_config(self.request.registry.settings)
259 result = searcher.search(
260 result = searcher.search(
260 'commit_id:%s*' % commit_hash, 'commit', auth_user,
261 'commit_id:%s*' % commit_hash, 'commit', auth_user,
261 raise_on_exc=False)
262 raise_on_exc=False)
262
263
263 return [
264 return [
264 {
265 {
265 'id': entry['commit_id'],
266 'id': entry['commit_id'],
266 'value': org_query,
267 'value': org_query,
267 'value_display': 'repo `{}` commit: {}'.format(
268 'value_display': 'repo `{}` commit: {}'.format(
268 entry['repository'], entry['commit_id']),
269 entry['repository'], entry['commit_id']),
269 'type': 'commit',
270 'type': 'commit',
270 'repo': entry['repository'],
271 'repo': entry['repository'],
271 'url': h.route_path(
272 'url': h.route_path(
272 'repo_commit',
273 'repo_commit',
273 repo_name=entry['repository'], commit_id=entry['commit_id'])
274 repo_name=entry['repository'], commit_id=entry['commit_id'])
274 }
275 }
275 for entry in result['results']]
276 for entry in result['results']]
276
277
277 @LoginRequired()
278 @LoginRequired()
278 @view_config(
279 @view_config(
279 route_name='repo_list_data', request_method='GET',
280 route_name='repo_list_data', request_method='GET',
280 renderer='json_ext', xhr=True)
281 renderer='json_ext', xhr=True)
281 def repo_list_data(self):
282 def repo_list_data(self):
282 _ = self.request.translate
283 _ = self.request.translate
283 self.load_default_context()
284 self.load_default_context()
284
285
285 query = self.request.GET.get('query')
286 query = self.request.GET.get('query')
286 repo_type = self.request.GET.get('repo_type')
287 repo_type = self.request.GET.get('repo_type')
287 log.debug('generating repo list, query:%s, repo_type:%s',
288 log.debug('generating repo list, query:%s, repo_type:%s',
288 query, repo_type)
289 query, repo_type)
289
290
290 res = []
291 res = []
291 repos = self._get_repo_list(query, repo_type=repo_type)
292 repos = self._get_repo_list(query, repo_type=repo_type)
292 if repos:
293 if repos:
293 res.append({
294 res.append({
294 'text': _('Repositories'),
295 'text': _('Repositories'),
295 'children': repos
296 'children': repos
296 })
297 })
297
298
298 data = {
299 data = {
299 'more': False,
300 'more': False,
300 'results': res
301 'results': res
301 }
302 }
302 return data
303 return data
303
304
304 @LoginRequired()
305 @LoginRequired()
305 @view_config(
306 @view_config(
306 route_name='goto_switcher_data', request_method='GET',
307 route_name='goto_switcher_data', request_method='GET',
307 renderer='json_ext', xhr=True)
308 renderer='json_ext', xhr=True)
308 def goto_switcher_data(self):
309 def goto_switcher_data(self):
309 c = self.load_default_context()
310 c = self.load_default_context()
310
311
311 _ = self.request.translate
312 _ = self.request.translate
312
313
313 query = self.request.GET.get('query')
314 query = self.request.GET.get('query')
314 log.debug('generating main filter data, query %s', query)
315 log.debug('generating main filter data, query %s', query)
315
316
316 default_search_val = u'Full text search for: `{}`'.format(query)
317 default_search_val = u'Full text search for: `{}`'.format(query)
317 res = []
318 res = []
318 if not query:
319 if not query:
319 return {'suggestions': res}
320 return {'suggestions': res}
320
321
321 res.append({
322 res.append({
322 'id': -1,
323 'id': -1,
323 'value': query,
324 'value': query,
324 'value_display': default_search_val,
325 'value_display': default_search_val,
325 'type': 'search',
326 'type': 'search',
326 'url': h.route_path(
327 'url': h.route_path(
327 'search', _query={'q': query})
328 'search', _query={'q': query})
328 })
329 })
329 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
330 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
330 if repo_group_id:
331 if repo_group_id:
331 repo_group = RepoGroup.get(repo_group_id)
332 repo_group = RepoGroup.get(repo_group_id)
332 composed_hint = '{}/{}'.format(repo_group.group_name, query)
333 composed_hint = '{}/{}'.format(repo_group.group_name, query)
333 show_hint = not query.startswith(repo_group.group_name)
334 show_hint = not query.startswith(repo_group.group_name)
334 if repo_group and show_hint:
335 if repo_group and show_hint:
335 hint = u'Group search: `{}`'.format(composed_hint)
336 hint = u'Group search: `{}`'.format(composed_hint)
336 res.append({
337 res.append({
337 'id': -1,
338 'id': -1,
338 'value': composed_hint,
339 'value': composed_hint,
339 'value_display': hint,
340 'value_display': hint,
340 'type': 'hint',
341 'type': 'hint',
341 'url': ""
342 'url': ""
342 })
343 })
343
344
344 repo_groups = self._get_repo_group_list(query)
345 repo_groups = self._get_repo_group_list(query)
345 for serialized_repo_group in repo_groups:
346 for serialized_repo_group in repo_groups:
346 res.append(serialized_repo_group)
347 res.append(serialized_repo_group)
347
348
348 repos = self._get_repo_list(query)
349 repos = self._get_repo_list(query)
349 for serialized_repo in repos:
350 for serialized_repo in repos:
350 res.append(serialized_repo)
351 res.append(serialized_repo)
351
352
352 # TODO(marcink): permissions for that ?
353 # TODO(marcink): permissions for that ?
353 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
354 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
354 if allowed_user_search:
355 if allowed_user_search:
355 users = self._get_user_list(query)
356 users = self._get_user_list(query)
356 for serialized_user in users:
357 for serialized_user in users:
357 res.append(serialized_user)
358 res.append(serialized_user)
358
359
359 user_groups = self._get_user_groups_list(query)
360 user_groups = self._get_user_groups_list(query)
360 for serialized_user_group in user_groups:
361 for serialized_user_group in user_groups:
361 res.append(serialized_user_group)
362 res.append(serialized_user_group)
362
363
363 commits = self._get_hash_commit_list(c.auth_user, query)
364 commits = self._get_hash_commit_list(c.auth_user, query)
364 if commits:
365 if commits:
365 unique_repos = collections.OrderedDict()
366 unique_repos = collections.OrderedDict()
366 for commit in commits:
367 for commit in commits:
367 repo_name = commit['repo']
368 repo_name = commit['repo']
368 unique_repos.setdefault(repo_name, []).append(commit)
369 unique_repos.setdefault(repo_name, []).append(commit)
369
370
370 for repo, commits in unique_repos.items():
371 for repo, commits in unique_repos.items():
371 for commit in commits:
372 for commit in commits:
372 res.append(commit)
373 res.append(commit)
373
374
374 return {'suggestions': res}
375 return {'suggestions': res}
375
376
376 def _get_groups_and_repos(self, repo_group_id=None):
377 def _get_groups_and_repos(self, repo_group_id=None):
377 # repo groups groups
378 # repo groups groups
378 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
379 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
379 _perms = ['group.read', 'group.write', 'group.admin']
380 _perms = ['group.read', 'group.write', 'group.admin']
380 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
381 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
381 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
382 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
382 repo_group_list=repo_group_list_acl, admin=False)
383 repo_group_list=repo_group_list_acl, admin=False)
383
384
384 # repositories
385 # repositories
385 repo_list = Repository.get_all_repos(group_id=repo_group_id)
386 repo_list = Repository.get_all_repos(group_id=repo_group_id)
386 _perms = ['repository.read', 'repository.write', 'repository.admin']
387 _perms = ['repository.read', 'repository.write', 'repository.admin']
387 repo_list_acl = RepoList(repo_list, perm_set=_perms)
388 repo_list_acl = RepoList(repo_list, perm_set=_perms)
388 repo_data = RepoModel().get_repos_as_dict(
389 repo_data = RepoModel().get_repos_as_dict(
389 repo_list=repo_list_acl, admin=False)
390 repo_list=repo_list_acl, admin=False)
390
391
391 return repo_data, repo_group_data
392 return repo_data, repo_group_data
392
393
393 @LoginRequired()
394 @LoginRequired()
394 @view_config(
395 @view_config(
395 route_name='home', request_method='GET',
396 route_name='home', request_method='GET',
396 renderer='rhodecode:templates/index.mako')
397 renderer='rhodecode:templates/index.mako')
397 def main_page(self):
398 def main_page(self):
398 c = self.load_default_context()
399 c = self.load_default_context()
399 c.repo_group = None
400 c.repo_group = None
400
401
401 repo_data, repo_group_data = self._get_groups_and_repos()
402 repo_data, repo_group_data = self._get_groups_and_repos()
402 # json used to render the grids
403 # json used to render the grids
403 c.repos_data = json.dumps(repo_data)
404 c.repos_data = json.dumps(repo_data)
404 c.repo_groups_data = json.dumps(repo_group_data)
405 c.repo_groups_data = json.dumps(repo_group_data)
405
406
406 return self._get_template_context(c)
407 return self._get_template_context(c)
407
408
408 @LoginRequired()
409 @LoginRequired()
409 @HasRepoGroupPermissionAnyDecorator(
410 @HasRepoGroupPermissionAnyDecorator(
410 'group.read', 'group.write', 'group.admin')
411 'group.read', 'group.write', 'group.admin')
411 @view_config(
412 @view_config(
412 route_name='repo_group_home', request_method='GET',
413 route_name='repo_group_home', request_method='GET',
413 renderer='rhodecode:templates/index_repo_group.mako')
414 renderer='rhodecode:templates/index_repo_group.mako')
414 @view_config(
415 @view_config(
415 route_name='repo_group_home_slash', request_method='GET',
416 route_name='repo_group_home_slash', request_method='GET',
416 renderer='rhodecode:templates/index_repo_group.mako')
417 renderer='rhodecode:templates/index_repo_group.mako')
417 def repo_group_main_page(self):
418 def repo_group_main_page(self):
418 c = self.load_default_context()
419 c = self.load_default_context()
419 c.repo_group = self.request.db_repo_group
420 c.repo_group = self.request.db_repo_group
420 repo_data, repo_group_data = self._get_groups_and_repos(
421 repo_data, repo_group_data = self._get_groups_and_repos(
421 c.repo_group.group_id)
422 c.repo_group.group_id)
422
423
423 # json used to render the grids
424 # json used to render the grids
424 c.repos_data = json.dumps(repo_data)
425 c.repos_data = json.dumps(repo_data)
425 c.repo_groups_data = json.dumps(repo_group_data)
426 c.repo_groups_data = json.dumps(repo_group_data)
426
427
427 return self._get_template_context(c)
428 return self._get_template_context(c)
429
430 @LoginRequired()
431 @CSRFRequired()
432 @view_config(
433 route_name='markup_preview', request_method='POST',
434 renderer='string', xhr=True)
435 def markup_preview(self):
436 # Technically a CSRF token is not needed as no state changes with this
437 # call. However, as this is a POST is better to have it, so automated
438 # tools don't flag it as potential CSRF.
439 # Post is required because the payload could be bigger than the maximum
440 # allowed by GET.
441
442 text = self.request.POST.get('text')
443 renderer = self.request.POST.get('renderer') or 'rst'
444 if text:
445 return h.render(text, renderer=renderer, mentions=True)
446 return ''
@@ -1,2407 +1,2466 b''
1 //Primary CSS
1 //Primary CSS
2
2
3 //--- IMPORTS ------------------//
3 //--- IMPORTS ------------------//
4
4
5 @import 'helpers';
5 @import 'helpers';
6 @import 'mixins';
6 @import 'mixins';
7 @import 'rcicons';
7 @import 'rcicons';
8 @import 'fonts';
8 @import 'fonts';
9 @import 'variables';
9 @import 'variables';
10 @import 'bootstrap-variables';
10 @import 'bootstrap-variables';
11 @import 'form-bootstrap';
11 @import 'form-bootstrap';
12 @import 'codemirror';
12 @import 'codemirror';
13 @import 'legacy_code_styles';
13 @import 'legacy_code_styles';
14 @import 'readme-box';
14 @import 'readme-box';
15 @import 'progress-bar';
15 @import 'progress-bar';
16
16
17 @import 'type';
17 @import 'type';
18 @import 'alerts';
18 @import 'alerts';
19 @import 'buttons';
19 @import 'buttons';
20 @import 'tags';
20 @import 'tags';
21 @import 'code-block';
21 @import 'code-block';
22 @import 'examples';
22 @import 'examples';
23 @import 'login';
23 @import 'login';
24 @import 'main-content';
24 @import 'main-content';
25 @import 'select2';
25 @import 'select2';
26 @import 'comments';
26 @import 'comments';
27 @import 'panels-bootstrap';
27 @import 'panels-bootstrap';
28 @import 'panels';
28 @import 'panels';
29 @import 'deform';
29 @import 'deform';
30
30
31 //--- BASE ------------------//
31 //--- BASE ------------------//
32 .noscript-error {
32 .noscript-error {
33 top: 0;
33 top: 0;
34 left: 0;
34 left: 0;
35 width: 100%;
35 width: 100%;
36 z-index: 101;
36 z-index: 101;
37 text-align: center;
37 text-align: center;
38 font-family: @text-semibold;
38 font-family: @text-semibold;
39 font-size: 120%;
39 font-size: 120%;
40 color: white;
40 color: white;
41 background-color: @alert2;
41 background-color: @alert2;
42 padding: 5px 0 5px 0;
42 padding: 5px 0 5px 0;
43 }
43 }
44
44
45 html {
45 html {
46 display: table;
46 display: table;
47 height: 100%;
47 height: 100%;
48 width: 100%;
48 width: 100%;
49 }
49 }
50
50
51 body {
51 body {
52 display: table-cell;
52 display: table-cell;
53 width: 100%;
53 width: 100%;
54 }
54 }
55
55
56 //--- LAYOUT ------------------//
56 //--- LAYOUT ------------------//
57
57
58 .hidden{
58 .hidden{
59 display: none !important;
59 display: none !important;
60 }
60 }
61
61
62 .box{
62 .box{
63 float: left;
63 float: left;
64 width: 100%;
64 width: 100%;
65 }
65 }
66
66
67 .browser-header {
67 .browser-header {
68 clear: both;
68 clear: both;
69 }
69 }
70 .main {
70 .main {
71 clear: both;
71 clear: both;
72 padding:0 0 @pagepadding;
72 padding:0 0 @pagepadding;
73 height: auto;
73 height: auto;
74
74
75 &:after { //clearfix
75 &:after { //clearfix
76 content:"";
76 content:"";
77 clear:both;
77 clear:both;
78 width:100%;
78 width:100%;
79 display:block;
79 display:block;
80 }
80 }
81 }
81 }
82
82
83 .action-link{
83 .action-link{
84 margin-left: @padding;
84 margin-left: @padding;
85 padding-left: @padding;
85 padding-left: @padding;
86 border-left: @border-thickness solid @border-default-color;
86 border-left: @border-thickness solid @border-default-color;
87 }
87 }
88
88
89 input + .action-link, .action-link.first{
89 input + .action-link, .action-link.first{
90 border-left: none;
90 border-left: none;
91 }
91 }
92
92
93 .action-link.last{
93 .action-link.last{
94 margin-right: @padding;
94 margin-right: @padding;
95 padding-right: @padding;
95 padding-right: @padding;
96 }
96 }
97
97
98 .action-link.active,
98 .action-link.active,
99 .action-link.active a{
99 .action-link.active a{
100 color: @grey4;
100 color: @grey4;
101 }
101 }
102
102
103 .action-link.disabled {
103 .action-link.disabled {
104 color: @grey4;
104 color: @grey4;
105 cursor: inherit;
105 cursor: inherit;
106 }
106 }
107
107
108 .clipboard-action {
108 .clipboard-action {
109 cursor: pointer;
109 cursor: pointer;
110 }
110 }
111
111
112 ul.simple-list{
112 ul.simple-list{
113 list-style: none;
113 list-style: none;
114 margin: 0;
114 margin: 0;
115 padding: 0;
115 padding: 0;
116 }
116 }
117
117
118 .main-content {
118 .main-content {
119 padding-bottom: @pagepadding;
119 padding-bottom: @pagepadding;
120 }
120 }
121
121
122 .wide-mode-wrapper {
122 .wide-mode-wrapper {
123 max-width:4000px !important;
123 max-width:4000px !important;
124 }
124 }
125
125
126 .wrapper {
126 .wrapper {
127 position: relative;
127 position: relative;
128 max-width: @wrapper-maxwidth;
128 max-width: @wrapper-maxwidth;
129 margin: 0 auto;
129 margin: 0 auto;
130 }
130 }
131
131
132 #content {
132 #content {
133 clear: both;
133 clear: both;
134 padding: 0 @contentpadding;
134 padding: 0 @contentpadding;
135 }
135 }
136
136
137 .advanced-settings-fields{
137 .advanced-settings-fields{
138 input{
138 input{
139 margin-left: @textmargin;
139 margin-left: @textmargin;
140 margin-right: @padding/2;
140 margin-right: @padding/2;
141 }
141 }
142 }
142 }
143
143
144 .cs_files_title {
144 .cs_files_title {
145 margin: @pagepadding 0 0;
145 margin: @pagepadding 0 0;
146 }
146 }
147
147
148 input.inline[type="file"] {
148 input.inline[type="file"] {
149 display: inline;
149 display: inline;
150 }
150 }
151
151
152 .error_page {
152 .error_page {
153 margin: 10% auto;
153 margin: 10% auto;
154
154
155 h1 {
155 h1 {
156 color: @grey2;
156 color: @grey2;
157 }
157 }
158
158
159 .alert {
159 .alert {
160 margin: @padding 0;
160 margin: @padding 0;
161 }
161 }
162
162
163 .error-branding {
163 .error-branding {
164 font-family: @text-semibold;
164 font-family: @text-semibold;
165 color: @grey4;
165 color: @grey4;
166 }
166 }
167
167
168 .error_message {
168 .error_message {
169 font-family: @text-regular;
169 font-family: @text-regular;
170 }
170 }
171
171
172 .sidebar {
172 .sidebar {
173 min-height: 275px;
173 min-height: 275px;
174 margin: 0;
174 margin: 0;
175 padding: 0 0 @sidebarpadding @sidebarpadding;
175 padding: 0 0 @sidebarpadding @sidebarpadding;
176 border: none;
176 border: none;
177 }
177 }
178
178
179 .main-content {
179 .main-content {
180 position: relative;
180 position: relative;
181 margin: 0 @sidebarpadding @sidebarpadding;
181 margin: 0 @sidebarpadding @sidebarpadding;
182 padding: 0 0 0 @sidebarpadding;
182 padding: 0 0 0 @sidebarpadding;
183 border-left: @border-thickness solid @grey5;
183 border-left: @border-thickness solid @grey5;
184
184
185 @media (max-width:767px) {
185 @media (max-width:767px) {
186 clear: both;
186 clear: both;
187 width: 100%;
187 width: 100%;
188 margin: 0;
188 margin: 0;
189 border: none;
189 border: none;
190 }
190 }
191 }
191 }
192
192
193 .inner-column {
193 .inner-column {
194 float: left;
194 float: left;
195 width: 29.75%;
195 width: 29.75%;
196 min-height: 150px;
196 min-height: 150px;
197 margin: @sidebarpadding 2% 0 0;
197 margin: @sidebarpadding 2% 0 0;
198 padding: 0 2% 0 0;
198 padding: 0 2% 0 0;
199 border-right: @border-thickness solid @grey5;
199 border-right: @border-thickness solid @grey5;
200
200
201 @media (max-width:767px) {
201 @media (max-width:767px) {
202 clear: both;
202 clear: both;
203 width: 100%;
203 width: 100%;
204 border: none;
204 border: none;
205 }
205 }
206
206
207 ul {
207 ul {
208 padding-left: 1.25em;
208 padding-left: 1.25em;
209 }
209 }
210
210
211 &:last-child {
211 &:last-child {
212 margin: @sidebarpadding 0 0;
212 margin: @sidebarpadding 0 0;
213 border: none;
213 border: none;
214 }
214 }
215
215
216 h4 {
216 h4 {
217 margin: 0 0 @padding;
217 margin: 0 0 @padding;
218 font-family: @text-semibold;
218 font-family: @text-semibold;
219 }
219 }
220 }
220 }
221 }
221 }
222 .error-page-logo {
222 .error-page-logo {
223 width: 130px;
223 width: 130px;
224 height: 160px;
224 height: 160px;
225 }
225 }
226
226
227 // HEADER
227 // HEADER
228 .header {
228 .header {
229
229
230 // TODO: johbo: Fix login pages, so that they work without a min-height
230 // TODO: johbo: Fix login pages, so that they work without a min-height
231 // for the header and then remove the min-height. I chose a smaller value
231 // for the header and then remove the min-height. I chose a smaller value
232 // intentionally here to avoid rendering issues in the main navigation.
232 // intentionally here to avoid rendering issues in the main navigation.
233 min-height: 49px;
233 min-height: 49px;
234
234
235 position: relative;
235 position: relative;
236 vertical-align: bottom;
236 vertical-align: bottom;
237 padding: 0 @header-padding;
237 padding: 0 @header-padding;
238 background-color: @grey2;
238 background-color: @grey2;
239 color: @grey5;
239 color: @grey5;
240
240
241 .title {
241 .title {
242 overflow: visible;
242 overflow: visible;
243 }
243 }
244
244
245 &:before,
245 &:before,
246 &:after {
246 &:after {
247 content: "";
247 content: "";
248 clear: both;
248 clear: both;
249 width: 100%;
249 width: 100%;
250 }
250 }
251
251
252 // TODO: johbo: Avoids breaking "Repositories" chooser
252 // TODO: johbo: Avoids breaking "Repositories" chooser
253 .select2-container .select2-choice .select2-arrow {
253 .select2-container .select2-choice .select2-arrow {
254 display: none;
254 display: none;
255 }
255 }
256 }
256 }
257
257
258 #header-inner {
258 #header-inner {
259 &.title {
259 &.title {
260 margin: 0;
260 margin: 0;
261 }
261 }
262 &:before,
262 &:before,
263 &:after {
263 &:after {
264 content: "";
264 content: "";
265 clear: both;
265 clear: both;
266 }
266 }
267 }
267 }
268
268
269 // Gists
269 // Gists
270 #files_data {
270 #files_data {
271 clear: both; //for firefox
271 clear: both; //for firefox
272 }
272 }
273 #gistid {
273 #gistid {
274 margin-right: @padding;
274 margin-right: @padding;
275 }
275 }
276
276
277 // Global Settings Editor
277 // Global Settings Editor
278 .textarea.editor {
278 .textarea.editor {
279 float: left;
279 float: left;
280 position: relative;
280 position: relative;
281 max-width: @texteditor-width;
281 max-width: @texteditor-width;
282
282
283 select {
283 select {
284 position: absolute;
284 position: absolute;
285 top:10px;
285 top:10px;
286 right:0;
286 right:0;
287 }
287 }
288
288
289 .CodeMirror {
289 .CodeMirror {
290 margin: 0;
290 margin: 0;
291 }
291 }
292
292
293 .help-block {
293 .help-block {
294 margin: 0 0 @padding;
294 margin: 0 0 @padding;
295 padding:.5em;
295 padding:.5em;
296 background-color: @grey6;
296 background-color: @grey6;
297 &.pre-formatting {
297 &.pre-formatting {
298 white-space: pre;
298 white-space: pre;
299 }
299 }
300 }
300 }
301 }
301 }
302
302
303 ul.auth_plugins {
303 ul.auth_plugins {
304 margin: @padding 0 @padding @legend-width;
304 margin: @padding 0 @padding @legend-width;
305 padding: 0;
305 padding: 0;
306
306
307 li {
307 li {
308 margin-bottom: @padding;
308 margin-bottom: @padding;
309 line-height: 1em;
309 line-height: 1em;
310 list-style-type: none;
310 list-style-type: none;
311
311
312 .auth_buttons .btn {
312 .auth_buttons .btn {
313 margin-right: @padding;
313 margin-right: @padding;
314 }
314 }
315
315
316 &:before { content: none; }
316 &:before { content: none; }
317 }
317 }
318 }
318 }
319
319
320
320
321 // My Account PR list
321 // My Account PR list
322
322
323 #show_closed {
323 #show_closed {
324 margin: 0 1em 0 0;
324 margin: 0 1em 0 0;
325 }
325 }
326
326
327 .pullrequestlist {
327 .pullrequestlist {
328 .closed {
328 .closed {
329 background-color: @grey6;
329 background-color: @grey6;
330 }
330 }
331 .td-status {
331 .td-status {
332 padding-left: .5em;
332 padding-left: .5em;
333 }
333 }
334 .log-container .truncate {
334 .log-container .truncate {
335 height: 2.75em;
335 height: 2.75em;
336 white-space: pre-line;
336 white-space: pre-line;
337 }
337 }
338 table.rctable .user {
338 table.rctable .user {
339 padding-left: 0;
339 padding-left: 0;
340 }
340 }
341 table.rctable {
341 table.rctable {
342 td.td-description,
342 td.td-description,
343 .rc-user {
343 .rc-user {
344 min-width: auto;
344 min-width: auto;
345 }
345 }
346 }
346 }
347 }
347 }
348
348
349 // Pull Requests
349 // Pull Requests
350
350
351 .pullrequests_section_head {
351 .pullrequests_section_head {
352 display: block;
352 display: block;
353 clear: both;
353 clear: both;
354 margin: @padding 0;
354 margin: @padding 0;
355 font-family: @text-bold;
355 font-family: @text-bold;
356 }
356 }
357
357
358 .pr-origininfo, .pr-targetinfo {
358 .pr-origininfo, .pr-targetinfo {
359 position: relative;
359 position: relative;
360
360
361 .tag {
361 .tag {
362 display: inline-block;
362 display: inline-block;
363 margin: 0 1em .5em 0;
363 margin: 0 1em .5em 0;
364 }
364 }
365
365
366 .clone-url {
366 .clone-url {
367 display: inline-block;
367 display: inline-block;
368 margin: 0 0 .5em 0;
368 margin: 0 0 .5em 0;
369 padding: 0;
369 padding: 0;
370 line-height: 1.2em;
370 line-height: 1.2em;
371 }
371 }
372 }
372 }
373
373
374 .pr-mergeinfo {
374 .pr-mergeinfo {
375 min-width: 95% !important;
375 min-width: 95% !important;
376 padding: 0 !important;
376 padding: 0 !important;
377 border: 0;
377 border: 0;
378 }
378 }
379 .pr-mergeinfo-copy {
379 .pr-mergeinfo-copy {
380 padding: 0 0;
380 padding: 0 0;
381 }
381 }
382
382
383 .pr-pullinfo {
383 .pr-pullinfo {
384 min-width: 95% !important;
384 min-width: 95% !important;
385 padding: 0 !important;
385 padding: 0 !important;
386 border: 0;
386 border: 0;
387 }
387 }
388 .pr-pullinfo-copy {
388 .pr-pullinfo-copy {
389 padding: 0 0;
389 padding: 0 0;
390 }
390 }
391
391
392
392
393 #pr-title-input {
393 #pr-title-input {
394 width: 72%;
394 width: 72%;
395 font-size: 1em;
395 font-size: 1em;
396 font-family: @text-bold;
396 font-family: @text-bold;
397 margin: 0;
397 margin: 0;
398 padding: 0 0 0 @padding/4;
398 padding: 0 0 0 @padding/4;
399 line-height: 1.7em;
399 line-height: 1.7em;
400 color: @text-color;
400 color: @text-color;
401 letter-spacing: .02em;
401 letter-spacing: .02em;
402 }
402 }
403
403
404 #pullrequest_title {
404 #pullrequest_title {
405 width: 100%;
405 width: 100%;
406 box-sizing: border-box;
406 box-sizing: border-box;
407 }
407 }
408
408
409 #pr_open_message {
409 #pr_open_message {
410 border: @border-thickness solid #fff;
410 border: @border-thickness solid #fff;
411 border-radius: @border-radius;
411 border-radius: @border-radius;
412 padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0;
412 padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0;
413 text-align: left;
413 text-align: left;
414 overflow: hidden;
414 overflow: hidden;
415 }
415 }
416
416
417 .pr-submit-button {
417 .pr-submit-button {
418 float: right;
418 float: right;
419 margin: 0 0 0 5px;
419 margin: 0 0 0 5px;
420 }
420 }
421
421
422 .pr-spacing-container {
422 .pr-spacing-container {
423 padding: 20px;
423 padding: 20px;
424 clear: both
424 clear: both
425 }
425 }
426
426
427 #pr-description-input {
427 #pr-description-input {
428 margin-bottom: 0;
428 margin-bottom: 0;
429 }
429 }
430
430
431 .pr-description-label {
431 .pr-description-label {
432 vertical-align: top;
432 vertical-align: top;
433 }
433 }
434
434
435 .perms_section_head {
435 .perms_section_head {
436 min-width: 625px;
436 min-width: 625px;
437
437
438 h2 {
438 h2 {
439 margin-bottom: 0;
439 margin-bottom: 0;
440 }
440 }
441
441
442 .label-checkbox {
442 .label-checkbox {
443 float: left;
443 float: left;
444 }
444 }
445
445
446 &.field {
446 &.field {
447 margin: @space 0 @padding;
447 margin: @space 0 @padding;
448 }
448 }
449
449
450 &:first-child.field {
450 &:first-child.field {
451 margin-top: 0;
451 margin-top: 0;
452
452
453 .label {
453 .label {
454 margin-top: 0;
454 margin-top: 0;
455 padding-top: 0;
455 padding-top: 0;
456 }
456 }
457
457
458 .radios {
458 .radios {
459 padding-top: 0;
459 padding-top: 0;
460 }
460 }
461 }
461 }
462
462
463 .radios {
463 .radios {
464 position: relative;
464 position: relative;
465 width: 405px;
465 width: 405px;
466 }
466 }
467 }
467 }
468
468
469 //--- MODULES ------------------//
469 //--- MODULES ------------------//
470
470
471
471
472 // Server Announcement
472 // Server Announcement
473 #server-announcement {
473 #server-announcement {
474 width: 95%;
474 width: 95%;
475 margin: @padding auto;
475 margin: @padding auto;
476 padding: @padding;
476 padding: @padding;
477 border-width: 2px;
477 border-width: 2px;
478 border-style: solid;
478 border-style: solid;
479 .border-radius(2px);
479 .border-radius(2px);
480 font-family: @text-bold;
480 font-family: @text-bold;
481
481
482 &.info { border-color: @alert4; background-color: @alert4-inner; }
482 &.info { border-color: @alert4; background-color: @alert4-inner; }
483 &.warning { border-color: @alert3; background-color: @alert3-inner; }
483 &.warning { border-color: @alert3; background-color: @alert3-inner; }
484 &.error { border-color: @alert2; background-color: @alert2-inner; }
484 &.error { border-color: @alert2; background-color: @alert2-inner; }
485 &.success { border-color: @alert1; background-color: @alert1-inner; }
485 &.success { border-color: @alert1; background-color: @alert1-inner; }
486 &.neutral { border-color: @grey3; background-color: @grey6; }
486 &.neutral { border-color: @grey3; background-color: @grey6; }
487 }
487 }
488
488
489 // Fixed Sidebar Column
489 // Fixed Sidebar Column
490 .sidebar-col-wrapper {
490 .sidebar-col-wrapper {
491 padding-left: @sidebar-all-width;
491 padding-left: @sidebar-all-width;
492
492
493 .sidebar {
493 .sidebar {
494 width: @sidebar-width;
494 width: @sidebar-width;
495 margin-left: -@sidebar-all-width;
495 margin-left: -@sidebar-all-width;
496 }
496 }
497 }
497 }
498
498
499 .sidebar-col-wrapper.scw-small {
499 .sidebar-col-wrapper.scw-small {
500 padding-left: @sidebar-small-all-width;
500 padding-left: @sidebar-small-all-width;
501
501
502 .sidebar {
502 .sidebar {
503 width: @sidebar-small-width;
503 width: @sidebar-small-width;
504 margin-left: -@sidebar-small-all-width;
504 margin-left: -@sidebar-small-all-width;
505 }
505 }
506 }
506 }
507
507
508
508
509 // FOOTER
509 // FOOTER
510 #footer {
510 #footer {
511 padding: 0;
511 padding: 0;
512 text-align: center;
512 text-align: center;
513 vertical-align: middle;
513 vertical-align: middle;
514 color: @grey2;
514 color: @grey2;
515 background-color: @grey6;
515 background-color: @grey6;
516
516
517 p {
517 p {
518 margin: 0;
518 margin: 0;
519 padding: 1em;
519 padding: 1em;
520 line-height: 1em;
520 line-height: 1em;
521 }
521 }
522
522
523 .server-instance { //server instance
523 .server-instance { //server instance
524 display: none;
524 display: none;
525 }
525 }
526
526
527 .title {
527 .title {
528 float: none;
528 float: none;
529 margin: 0 auto;
529 margin: 0 auto;
530 }
530 }
531 }
531 }
532
532
533 button.close {
533 button.close {
534 padding: 0;
534 padding: 0;
535 cursor: pointer;
535 cursor: pointer;
536 background: transparent;
536 background: transparent;
537 border: 0;
537 border: 0;
538 .box-shadow(none);
538 .box-shadow(none);
539 -webkit-appearance: none;
539 -webkit-appearance: none;
540 }
540 }
541
541
542 .close {
542 .close {
543 float: right;
543 float: right;
544 font-size: 21px;
544 font-size: 21px;
545 font-family: @text-bootstrap;
545 font-family: @text-bootstrap;
546 line-height: 1em;
546 line-height: 1em;
547 font-weight: bold;
547 font-weight: bold;
548 color: @grey2;
548 color: @grey2;
549
549
550 &:hover,
550 &:hover,
551 &:focus {
551 &:focus {
552 color: @grey1;
552 color: @grey1;
553 text-decoration: none;
553 text-decoration: none;
554 cursor: pointer;
554 cursor: pointer;
555 }
555 }
556 }
556 }
557
557
558 // GRID
558 // GRID
559 .sorting,
559 .sorting,
560 .sorting_desc,
560 .sorting_desc,
561 .sorting_asc {
561 .sorting_asc {
562 cursor: pointer;
562 cursor: pointer;
563 }
563 }
564 .sorting_desc:after {
564 .sorting_desc:after {
565 content: "\00A0\25B2";
565 content: "\00A0\25B2";
566 font-size: .75em;
566 font-size: .75em;
567 }
567 }
568 .sorting_asc:after {
568 .sorting_asc:after {
569 content: "\00A0\25BC";
569 content: "\00A0\25BC";
570 font-size: .68em;
570 font-size: .68em;
571 }
571 }
572
572
573
573
574 .user_auth_tokens {
574 .user_auth_tokens {
575
575
576 &.truncate {
576 &.truncate {
577 white-space: nowrap;
577 white-space: nowrap;
578 overflow: hidden;
578 overflow: hidden;
579 text-overflow: ellipsis;
579 text-overflow: ellipsis;
580 }
580 }
581
581
582 .fields .field .input {
582 .fields .field .input {
583 margin: 0;
583 margin: 0;
584 }
584 }
585
585
586 input#description {
586 input#description {
587 width: 100px;
587 width: 100px;
588 margin: 0;
588 margin: 0;
589 }
589 }
590
590
591 .drop-menu {
591 .drop-menu {
592 // TODO: johbo: Remove this, should work out of the box when
592 // TODO: johbo: Remove this, should work out of the box when
593 // having multiple inputs inline
593 // having multiple inputs inline
594 margin: 0 0 0 5px;
594 margin: 0 0 0 5px;
595 }
595 }
596 }
596 }
597 #user_list_table {
597 #user_list_table {
598 .closed {
598 .closed {
599 background-color: @grey6;
599 background-color: @grey6;
600 }
600 }
601 }
601 }
602
602
603
603
604 input {
604 input {
605 &.disabled {
605 &.disabled {
606 opacity: .5;
606 opacity: .5;
607 }
607 }
608 }
608 }
609
609
610 // remove extra padding in firefox
610 // remove extra padding in firefox
611 input::-moz-focus-inner { border:0; padding:0 }
611 input::-moz-focus-inner { border:0; padding:0 }
612
612
613 .adjacent input {
613 .adjacent input {
614 margin-bottom: @padding;
614 margin-bottom: @padding;
615 }
615 }
616
616
617 .permissions_boxes {
617 .permissions_boxes {
618 display: block;
618 display: block;
619 }
619 }
620
620
621 //TODO: lisa: this should be in tables
621 //TODO: lisa: this should be in tables
622 .show_more_col {
622 .show_more_col {
623 width: 20px;
623 width: 20px;
624 }
624 }
625
625
626 //FORMS
626 //FORMS
627
627
628 .medium-inline,
628 .medium-inline,
629 input#description.medium-inline {
629 input#description.medium-inline {
630 display: inline;
630 display: inline;
631 width: @medium-inline-input-width;
631 width: @medium-inline-input-width;
632 min-width: 100px;
632 min-width: 100px;
633 }
633 }
634
634
635 select {
635 select {
636 //reset
636 //reset
637 -webkit-appearance: none;
637 -webkit-appearance: none;
638 -moz-appearance: none;
638 -moz-appearance: none;
639
639
640 display: inline-block;
640 display: inline-block;
641 height: 28px;
641 height: 28px;
642 width: auto;
642 width: auto;
643 margin: 0 @padding @padding 0;
643 margin: 0 @padding @padding 0;
644 padding: 0 18px 0 8px;
644 padding: 0 18px 0 8px;
645 line-height:1em;
645 line-height:1em;
646 font-size: @basefontsize;
646 font-size: @basefontsize;
647 border: @border-thickness solid @rcblue;
647 border: @border-thickness solid @rcblue;
648 background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%;
648 background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%;
649 color: @rcblue;
649 color: @rcblue;
650
650
651 &:after {
651 &:after {
652 content: "\00A0\25BE";
652 content: "\00A0\25BE";
653 }
653 }
654
654
655 &:focus {
655 &:focus {
656 outline: none;
656 outline: none;
657 }
657 }
658 }
658 }
659
659
660 option {
660 option {
661 &:focus {
661 &:focus {
662 outline: none;
662 outline: none;
663 }
663 }
664 }
664 }
665
665
666 input,
666 input,
667 textarea {
667 textarea {
668 padding: @input-padding;
668 padding: @input-padding;
669 border: @input-border-thickness solid @border-highlight-color;
669 border: @input-border-thickness solid @border-highlight-color;
670 .border-radius (@border-radius);
670 .border-radius (@border-radius);
671 font-family: @text-light;
671 font-family: @text-light;
672 font-size: @basefontsize;
672 font-size: @basefontsize;
673
673
674 &.input-sm {
674 &.input-sm {
675 padding: 5px;
675 padding: 5px;
676 }
676 }
677
677
678 &#description {
678 &#description {
679 min-width: @input-description-minwidth;
679 min-width: @input-description-minwidth;
680 min-height: 1em;
680 min-height: 1em;
681 padding: 10px;
681 padding: 10px;
682 }
682 }
683 }
683 }
684
684
685 .field-sm {
685 .field-sm {
686 input,
686 input,
687 textarea {
687 textarea {
688 padding: 5px;
688 padding: 5px;
689 }
689 }
690 }
690 }
691
691
692 textarea {
692 textarea {
693 display: block;
693 display: block;
694 clear: both;
694 clear: both;
695 width: 100%;
695 width: 100%;
696 min-height: 100px;
696 min-height: 100px;
697 margin-bottom: @padding;
697 margin-bottom: @padding;
698 .box-sizing(border-box);
698 .box-sizing(border-box);
699 overflow: auto;
699 overflow: auto;
700 }
700 }
701
701
702 label {
702 label {
703 font-family: @text-light;
703 font-family: @text-light;
704 }
704 }
705
705
706 // GRAVATARS
706 // GRAVATARS
707 // centers gravatar on username to the right
707 // centers gravatar on username to the right
708
708
709 .gravatar {
709 .gravatar {
710 display: inline;
710 display: inline;
711 min-width: 16px;
711 min-width: 16px;
712 min-height: 16px;
712 min-height: 16px;
713 margin: -5px 0;
713 margin: -5px 0;
714 padding: 0;
714 padding: 0;
715 line-height: 1em;
715 line-height: 1em;
716 border: 1px solid @grey4;
716 border: 1px solid @grey4;
717 box-sizing: content-box;
717 box-sizing: content-box;
718
718
719 &.gravatar-large {
719 &.gravatar-large {
720 margin: -0.5em .25em -0.5em 0;
720 margin: -0.5em .25em -0.5em 0;
721 }
721 }
722
722
723 & + .user {
723 & + .user {
724 display: inline;
724 display: inline;
725 margin: 0;
725 margin: 0;
726 padding: 0 0 0 .17em;
726 padding: 0 0 0 .17em;
727 line-height: 1em;
727 line-height: 1em;
728 }
728 }
729 }
729 }
730
730
731 .user-inline-data {
731 .user-inline-data {
732 display: inline-block;
732 display: inline-block;
733 float: left;
733 float: left;
734 padding-left: .5em;
734 padding-left: .5em;
735 line-height: 1.3em;
735 line-height: 1.3em;
736 }
736 }
737
737
738 .rc-user { // gravatar + user wrapper
738 .rc-user { // gravatar + user wrapper
739 float: left;
739 float: left;
740 position: relative;
740 position: relative;
741 min-width: 100px;
741 min-width: 100px;
742 max-width: 200px;
742 max-width: 200px;
743 min-height: (@gravatar-size + @border-thickness * 2); // account for border
743 min-height: (@gravatar-size + @border-thickness * 2); // account for border
744 display: block;
744 display: block;
745 padding: 0 0 0 (@gravatar-size + @basefontsize/2 + @border-thickness * 2);
745 padding: 0 0 0 (@gravatar-size + @basefontsize/2 + @border-thickness * 2);
746
746
747
747
748 .gravatar {
748 .gravatar {
749 display: block;
749 display: block;
750 position: absolute;
750 position: absolute;
751 top: 0;
751 top: 0;
752 left: 0;
752 left: 0;
753 min-width: @gravatar-size;
753 min-width: @gravatar-size;
754 min-height: @gravatar-size;
754 min-height: @gravatar-size;
755 margin: 0;
755 margin: 0;
756 }
756 }
757
757
758 .user {
758 .user {
759 display: block;
759 display: block;
760 max-width: 175px;
760 max-width: 175px;
761 padding-top: 2px;
761 padding-top: 2px;
762 overflow: hidden;
762 overflow: hidden;
763 text-overflow: ellipsis;
763 text-overflow: ellipsis;
764 }
764 }
765 }
765 }
766
766
767 .gist-gravatar,
767 .gist-gravatar,
768 .journal_container {
768 .journal_container {
769 .gravatar-large {
769 .gravatar-large {
770 margin: 0 .5em -10px 0;
770 margin: 0 .5em -10px 0;
771 }
771 }
772 }
772 }
773
773
774
774
775 // ADMIN SETTINGS
775 // ADMIN SETTINGS
776
776
777 // Tag Patterns
777 // Tag Patterns
778 .tag_patterns {
778 .tag_patterns {
779 .tag_input {
779 .tag_input {
780 margin-bottom: @padding;
780 margin-bottom: @padding;
781 }
781 }
782 }
782 }
783
783
784 .locked_input {
784 .locked_input {
785 position: relative;
785 position: relative;
786
786
787 input {
787 input {
788 display: inline;
788 display: inline;
789 margin: 3px 5px 0px 0px;
789 margin: 3px 5px 0px 0px;
790 }
790 }
791
791
792 br {
792 br {
793 display: none;
793 display: none;
794 }
794 }
795
795
796 .error-message {
796 .error-message {
797 float: left;
797 float: left;
798 width: 100%;
798 width: 100%;
799 }
799 }
800
800
801 .lock_input_button {
801 .lock_input_button {
802 display: inline;
802 display: inline;
803 }
803 }
804
804
805 .help-block {
805 .help-block {
806 clear: both;
806 clear: both;
807 }
807 }
808 }
808 }
809
809
810 // Notifications
810 // Notifications
811
811
812 .notifications_buttons {
812 .notifications_buttons {
813 margin: 0 0 @space 0;
813 margin: 0 0 @space 0;
814 padding: 0;
814 padding: 0;
815
815
816 .btn {
816 .btn {
817 display: inline-block;
817 display: inline-block;
818 }
818 }
819 }
819 }
820
820
821 .notification-list {
821 .notification-list {
822
822
823 div {
823 div {
824 display: inline-block;
824 display: inline-block;
825 vertical-align: middle;
825 vertical-align: middle;
826 }
826 }
827
827
828 .container {
828 .container {
829 display: block;
829 display: block;
830 margin: 0 0 @padding 0;
830 margin: 0 0 @padding 0;
831 }
831 }
832
832
833 .delete-notifications {
833 .delete-notifications {
834 margin-left: @padding;
834 margin-left: @padding;
835 text-align: right;
835 text-align: right;
836 cursor: pointer;
836 cursor: pointer;
837 }
837 }
838
838
839 .read-notifications {
839 .read-notifications {
840 margin-left: @padding/2;
840 margin-left: @padding/2;
841 text-align: right;
841 text-align: right;
842 width: 35px;
842 width: 35px;
843 cursor: pointer;
843 cursor: pointer;
844 }
844 }
845
845
846 .icon-minus-sign {
846 .icon-minus-sign {
847 color: @alert2;
847 color: @alert2;
848 }
848 }
849
849
850 .icon-ok-sign {
850 .icon-ok-sign {
851 color: @alert1;
851 color: @alert1;
852 }
852 }
853 }
853 }
854
854
855 .user_settings {
855 .user_settings {
856 float: left;
856 float: left;
857 clear: both;
857 clear: both;
858 display: block;
858 display: block;
859 width: 100%;
859 width: 100%;
860
860
861 .gravatar_box {
861 .gravatar_box {
862 margin-bottom: @padding;
862 margin-bottom: @padding;
863
863
864 &:after {
864 &:after {
865 content: " ";
865 content: " ";
866 clear: both;
866 clear: both;
867 width: 100%;
867 width: 100%;
868 }
868 }
869 }
869 }
870
870
871 .fields .field {
871 .fields .field {
872 clear: both;
872 clear: both;
873 }
873 }
874 }
874 }
875
875
876 .advanced_settings {
876 .advanced_settings {
877 margin-bottom: @space;
877 margin-bottom: @space;
878
878
879 .help-block {
879 .help-block {
880 margin-left: 0;
880 margin-left: 0;
881 }
881 }
882
882
883 button + .help-block {
883 button + .help-block {
884 margin-top: @padding;
884 margin-top: @padding;
885 }
885 }
886 }
886 }
887
887
888 // admin settings radio buttons and labels
888 // admin settings radio buttons and labels
889 .label-2 {
889 .label-2 {
890 float: left;
890 float: left;
891 width: @label2-width;
891 width: @label2-width;
892
892
893 label {
893 label {
894 color: @grey1;
894 color: @grey1;
895 }
895 }
896 }
896 }
897 .checkboxes {
897 .checkboxes {
898 float: left;
898 float: left;
899 width: @checkboxes-width;
899 width: @checkboxes-width;
900 margin-bottom: @padding;
900 margin-bottom: @padding;
901
901
902 .checkbox {
902 .checkbox {
903 width: 100%;
903 width: 100%;
904
904
905 label {
905 label {
906 margin: 0;
906 margin: 0;
907 padding: 0;
907 padding: 0;
908 }
908 }
909 }
909 }
910
910
911 .checkbox + .checkbox {
911 .checkbox + .checkbox {
912 display: inline-block;
912 display: inline-block;
913 }
913 }
914
914
915 label {
915 label {
916 margin-right: 1em;
916 margin-right: 1em;
917 }
917 }
918 }
918 }
919
919
920 // CHANGELOG
920 // CHANGELOG
921 .container_header {
921 .container_header {
922 float: left;
922 float: left;
923 display: block;
923 display: block;
924 width: 100%;
924 width: 100%;
925 margin: @padding 0 @padding;
925 margin: @padding 0 @padding;
926
926
927 #filter_changelog {
927 #filter_changelog {
928 float: left;
928 float: left;
929 margin-right: @padding;
929 margin-right: @padding;
930 }
930 }
931
931
932 .breadcrumbs_light {
932 .breadcrumbs_light {
933 display: inline-block;
933 display: inline-block;
934 }
934 }
935 }
935 }
936
936
937 .info_box {
937 .info_box {
938 float: right;
938 float: right;
939 }
939 }
940
940
941
941
942 #graph_nodes {
942 #graph_nodes {
943 padding-top: 43px;
943 padding-top: 43px;
944 }
944 }
945
945
946 #graph_content{
946 #graph_content{
947
947
948 // adjust for table headers so that graph renders properly
948 // adjust for table headers so that graph renders properly
949 // #graph_nodes padding - table cell padding
949 // #graph_nodes padding - table cell padding
950 padding-top: (@space - (@basefontsize * 2.4));
950 padding-top: (@space - (@basefontsize * 2.4));
951
951
952 &.graph_full_width {
952 &.graph_full_width {
953 width: 100%;
953 width: 100%;
954 max-width: 100%;
954 max-width: 100%;
955 }
955 }
956 }
956 }
957
957
958 #graph {
958 #graph {
959 .flag_status {
959 .flag_status {
960 margin: 0;
960 margin: 0;
961 }
961 }
962
962
963 .pagination-left {
963 .pagination-left {
964 float: left;
964 float: left;
965 clear: both;
965 clear: both;
966 }
966 }
967
967
968 .log-container {
968 .log-container {
969 max-width: 345px;
969 max-width: 345px;
970
970
971 .message{
971 .message{
972 max-width: 340px;
972 max-width: 340px;
973 }
973 }
974 }
974 }
975
975
976 .graph-col-wrapper {
976 .graph-col-wrapper {
977 padding-left: 110px;
977 padding-left: 110px;
978
978
979 #graph_nodes {
979 #graph_nodes {
980 width: 100px;
980 width: 100px;
981 margin-left: -110px;
981 margin-left: -110px;
982 float: left;
982 float: left;
983 clear: left;
983 clear: left;
984 }
984 }
985 }
985 }
986
986
987 .load-more-commits {
987 .load-more-commits {
988 text-align: center;
988 text-align: center;
989 }
989 }
990 .load-more-commits:hover {
990 .load-more-commits:hover {
991 background-color: @grey7;
991 background-color: @grey7;
992 }
992 }
993 .load-more-commits {
993 .load-more-commits {
994 a {
994 a {
995 display: block;
995 display: block;
996 }
996 }
997 }
997 }
998 }
998 }
999
999
1000 #filter_changelog {
1000 #filter_changelog {
1001 float: left;
1001 float: left;
1002 }
1002 }
1003
1003
1004
1004
1005 //--- THEME ------------------//
1005 //--- THEME ------------------//
1006
1006
1007 #logo {
1007 #logo {
1008 float: left;
1008 float: left;
1009 margin: 9px 0 0 0;
1009 margin: 9px 0 0 0;
1010
1010
1011 .header {
1011 .header {
1012 background-color: transparent;
1012 background-color: transparent;
1013 }
1013 }
1014
1014
1015 a {
1015 a {
1016 display: inline-block;
1016 display: inline-block;
1017 }
1017 }
1018
1018
1019 img {
1019 img {
1020 height:30px;
1020 height:30px;
1021 }
1021 }
1022 }
1022 }
1023
1023
1024 .logo-wrapper {
1024 .logo-wrapper {
1025 float:left;
1025 float:left;
1026 }
1026 }
1027
1027
1028 .branding{
1028 .branding{
1029 float: left;
1029 float: left;
1030 padding: 9px 2px;
1030 padding: 9px 2px;
1031 line-height: 1em;
1031 line-height: 1em;
1032 font-size: @navigation-fontsize;
1032 font-size: @navigation-fontsize;
1033 }
1033 }
1034
1034
1035 img {
1035 img {
1036 border: none;
1036 border: none;
1037 outline: none;
1037 outline: none;
1038 }
1038 }
1039 user-profile-header
1039 user-profile-header
1040 label {
1040 label {
1041
1041
1042 input[type="checkbox"] {
1042 input[type="checkbox"] {
1043 margin-right: 1em;
1043 margin-right: 1em;
1044 }
1044 }
1045 input[type="radio"] {
1045 input[type="radio"] {
1046 margin-right: 1em;
1046 margin-right: 1em;
1047 }
1047 }
1048 }
1048 }
1049
1049
1050 .flag_status {
1050 .flag_status {
1051 margin: 2px 8px 6px 2px;
1051 margin: 2px 8px 6px 2px;
1052 &.under_review {
1052 &.under_review {
1053 .circle(5px, @alert3);
1053 .circle(5px, @alert3);
1054 }
1054 }
1055 &.approved {
1055 &.approved {
1056 .circle(5px, @alert1);
1056 .circle(5px, @alert1);
1057 }
1057 }
1058 &.rejected,
1058 &.rejected,
1059 &.forced_closed{
1059 &.forced_closed{
1060 .circle(5px, @alert2);
1060 .circle(5px, @alert2);
1061 }
1061 }
1062 &.not_reviewed {
1062 &.not_reviewed {
1063 .circle(5px, @grey5);
1063 .circle(5px, @grey5);
1064 }
1064 }
1065 }
1065 }
1066
1066
1067 .flag_status_comment_box {
1067 .flag_status_comment_box {
1068 margin: 5px 6px 0px 2px;
1068 margin: 5px 6px 0px 2px;
1069 }
1069 }
1070 .test_pattern_preview {
1070 .test_pattern_preview {
1071 margin: @space 0;
1071 margin: @space 0;
1072
1072
1073 p {
1073 p {
1074 margin-bottom: 0;
1074 margin-bottom: 0;
1075 border-bottom: @border-thickness solid @border-default-color;
1075 border-bottom: @border-thickness solid @border-default-color;
1076 color: @grey3;
1076 color: @grey3;
1077 }
1077 }
1078
1078
1079 .btn {
1079 .btn {
1080 margin-bottom: @padding;
1080 margin-bottom: @padding;
1081 }
1081 }
1082 }
1082 }
1083 #test_pattern_result {
1083 #test_pattern_result {
1084 display: none;
1084 display: none;
1085 &:extend(pre);
1085 &:extend(pre);
1086 padding: .9em;
1086 padding: .9em;
1087 color: @grey3;
1087 color: @grey3;
1088 background-color: @grey7;
1088 background-color: @grey7;
1089 border-right: @border-thickness solid @border-default-color;
1089 border-right: @border-thickness solid @border-default-color;
1090 border-bottom: @border-thickness solid @border-default-color;
1090 border-bottom: @border-thickness solid @border-default-color;
1091 border-left: @border-thickness solid @border-default-color;
1091 border-left: @border-thickness solid @border-default-color;
1092 }
1092 }
1093
1093
1094 #repo_vcs_settings {
1094 #repo_vcs_settings {
1095 #inherit_overlay_vcs_default {
1095 #inherit_overlay_vcs_default {
1096 display: none;
1096 display: none;
1097 }
1097 }
1098 #inherit_overlay_vcs_custom {
1098 #inherit_overlay_vcs_custom {
1099 display: custom;
1099 display: custom;
1100 }
1100 }
1101 &.inherited {
1101 &.inherited {
1102 #inherit_overlay_vcs_default {
1102 #inherit_overlay_vcs_default {
1103 display: block;
1103 display: block;
1104 }
1104 }
1105 #inherit_overlay_vcs_custom {
1105 #inherit_overlay_vcs_custom {
1106 display: none;
1106 display: none;
1107 }
1107 }
1108 }
1108 }
1109 }
1109 }
1110
1110
1111 .issue-tracker-link {
1111 .issue-tracker-link {
1112 color: @rcblue;
1112 color: @rcblue;
1113 }
1113 }
1114
1114
1115 // Issue Tracker Table Show/Hide
1115 // Issue Tracker Table Show/Hide
1116 #repo_issue_tracker {
1116 #repo_issue_tracker {
1117 #inherit_overlay {
1117 #inherit_overlay {
1118 display: none;
1118 display: none;
1119 }
1119 }
1120 #custom_overlay {
1120 #custom_overlay {
1121 display: custom;
1121 display: custom;
1122 }
1122 }
1123 &.inherited {
1123 &.inherited {
1124 #inherit_overlay {
1124 #inherit_overlay {
1125 display: block;
1125 display: block;
1126 }
1126 }
1127 #custom_overlay {
1127 #custom_overlay {
1128 display: none;
1128 display: none;
1129 }
1129 }
1130 }
1130 }
1131 }
1131 }
1132 table.issuetracker {
1132 table.issuetracker {
1133 &.readonly {
1133 &.readonly {
1134 tr, td {
1134 tr, td {
1135 color: @grey3;
1135 color: @grey3;
1136 }
1136 }
1137 }
1137 }
1138 .edit {
1138 .edit {
1139 display: none;
1139 display: none;
1140 }
1140 }
1141 .editopen {
1141 .editopen {
1142 .edit {
1142 .edit {
1143 display: inline;
1143 display: inline;
1144 }
1144 }
1145 .entry {
1145 .entry {
1146 display: none;
1146 display: none;
1147 }
1147 }
1148 }
1148 }
1149 tr td.td-action {
1149 tr td.td-action {
1150 min-width: 117px;
1150 min-width: 117px;
1151 }
1151 }
1152 td input {
1152 td input {
1153 max-width: none;
1153 max-width: none;
1154 min-width: 30px;
1154 min-width: 30px;
1155 width: 80%;
1155 width: 80%;
1156 }
1156 }
1157 .issuetracker_pref input {
1157 .issuetracker_pref input {
1158 width: 40%;
1158 width: 40%;
1159 }
1159 }
1160 input.edit_issuetracker_update {
1160 input.edit_issuetracker_update {
1161 margin-right: 0;
1161 margin-right: 0;
1162 width: auto;
1162 width: auto;
1163 }
1163 }
1164 }
1164 }
1165
1165
1166 table.integrations {
1166 table.integrations {
1167 .td-icon {
1167 .td-icon {
1168 width: 20px;
1168 width: 20px;
1169 .integration-icon {
1169 .integration-icon {
1170 height: 20px;
1170 height: 20px;
1171 width: 20px;
1171 width: 20px;
1172 }
1172 }
1173 }
1173 }
1174 }
1174 }
1175
1175
1176 .integrations {
1176 .integrations {
1177 a.integration-box {
1177 a.integration-box {
1178 color: @text-color;
1178 color: @text-color;
1179 &:hover {
1179 &:hover {
1180 .panel {
1180 .panel {
1181 background: #fbfbfb;
1181 background: #fbfbfb;
1182 }
1182 }
1183 }
1183 }
1184 .integration-icon {
1184 .integration-icon {
1185 width: 30px;
1185 width: 30px;
1186 height: 30px;
1186 height: 30px;
1187 margin-right: 20px;
1187 margin-right: 20px;
1188 float: left;
1188 float: left;
1189 }
1189 }
1190
1190
1191 .panel-body {
1191 .panel-body {
1192 padding: 10px;
1192 padding: 10px;
1193 }
1193 }
1194 .panel {
1194 .panel {
1195 margin-bottom: 10px;
1195 margin-bottom: 10px;
1196 }
1196 }
1197 h2 {
1197 h2 {
1198 display: inline-block;
1198 display: inline-block;
1199 margin: 0;
1199 margin: 0;
1200 min-width: 140px;
1200 min-width: 140px;
1201 }
1201 }
1202 }
1202 }
1203 a.integration-box.dummy-integration {
1203 a.integration-box.dummy-integration {
1204 color: @grey4
1204 color: @grey4
1205 }
1205 }
1206 }
1206 }
1207
1207
1208 //Permissions Settings
1208 //Permissions Settings
1209 #add_perm {
1209 #add_perm {
1210 margin: 0 0 @padding;
1210 margin: 0 0 @padding;
1211 cursor: pointer;
1211 cursor: pointer;
1212 }
1212 }
1213
1213
1214 .perm_ac {
1214 .perm_ac {
1215 input {
1215 input {
1216 width: 95%;
1216 width: 95%;
1217 }
1217 }
1218 }
1218 }
1219
1219
1220 .autocomplete-suggestions {
1220 .autocomplete-suggestions {
1221 width: auto !important; // overrides autocomplete.js
1221 width: auto !important; // overrides autocomplete.js
1222 margin: 0;
1222 margin: 0;
1223 border: @border-thickness solid @rcblue;
1223 border: @border-thickness solid @rcblue;
1224 border-radius: @border-radius;
1224 border-radius: @border-radius;
1225 color: @rcblue;
1225 color: @rcblue;
1226 background-color: white;
1226 background-color: white;
1227 }
1227 }
1228 .autocomplete-selected {
1228 .autocomplete-selected {
1229 background: #F0F0F0;
1229 background: #F0F0F0;
1230 }
1230 }
1231 .ac-container-wrap {
1231 .ac-container-wrap {
1232 margin: 0;
1232 margin: 0;
1233 padding: 8px;
1233 padding: 8px;
1234 border-bottom: @border-thickness solid @rclightblue;
1234 border-bottom: @border-thickness solid @rclightblue;
1235 list-style-type: none;
1235 list-style-type: none;
1236 cursor: pointer;
1236 cursor: pointer;
1237
1237
1238 &:hover {
1238 &:hover {
1239 background-color: @rclightblue;
1239 background-color: @rclightblue;
1240 }
1240 }
1241
1241
1242 img {
1242 img {
1243 height: @gravatar-size;
1243 height: @gravatar-size;
1244 width: @gravatar-size;
1244 width: @gravatar-size;
1245 margin-right: 1em;
1245 margin-right: 1em;
1246 }
1246 }
1247
1247
1248 strong {
1248 strong {
1249 font-weight: normal;
1249 font-weight: normal;
1250 }
1250 }
1251 }
1251 }
1252
1252
1253 // Settings Dropdown
1253 // Settings Dropdown
1254 .user-menu .container {
1254 .user-menu .container {
1255 padding: 0 4px;
1255 padding: 0 4px;
1256 margin: 0;
1256 margin: 0;
1257 }
1257 }
1258
1258
1259 .user-menu .gravatar {
1259 .user-menu .gravatar {
1260 cursor: pointer;
1260 cursor: pointer;
1261 }
1261 }
1262
1262
1263 .codeblock {
1263 .codeblock {
1264 margin-bottom: @padding;
1264 margin-bottom: @padding;
1265 clear: both;
1265 clear: both;
1266
1266
1267 .stats{
1267 .stats{
1268 overflow: hidden;
1268 overflow: hidden;
1269 }
1269 }
1270
1270
1271 .message{
1271 .message{
1272 textarea{
1272 textarea{
1273 margin: 0;
1273 margin: 0;
1274 }
1274 }
1275 }
1275 }
1276
1276
1277 .code-header {
1277 .code-header {
1278 .stats {
1278 .stats {
1279 line-height: 2em;
1279 line-height: 2em;
1280
1280
1281 .revision_id {
1281 .revision_id {
1282 margin-left: 0;
1282 margin-left: 0;
1283 }
1283 }
1284 .buttons {
1284 .buttons {
1285 padding-right: 0;
1285 padding-right: 0;
1286 }
1286 }
1287 }
1287 }
1288
1288
1289 .item{
1289 .item{
1290 margin-right: 0.5em;
1290 margin-right: 0.5em;
1291 }
1291 }
1292 }
1292 }
1293
1293
1294 #editor_container{
1294 #editor_container{
1295 position: relative;
1295 position: relative;
1296 margin: @padding;
1296 margin: @padding;
1297 }
1297 }
1298 }
1298 }
1299
1299
1300 #file_history_container {
1300 #file_history_container {
1301 display: none;
1301 display: none;
1302 }
1302 }
1303
1303
1304 .file-history-inner {
1304 .file-history-inner {
1305 margin-bottom: 10px;
1305 margin-bottom: 10px;
1306 }
1306 }
1307
1307
1308 // Pull Requests
1308 // Pull Requests
1309 .summary-details {
1309 .summary-details {
1310 width: 72%;
1310 width: 72%;
1311 }
1311 }
1312 .pr-summary {
1312 .pr-summary {
1313 border-bottom: @border-thickness solid @grey5;
1313 border-bottom: @border-thickness solid @grey5;
1314 margin-bottom: @space;
1314 margin-bottom: @space;
1315 }
1315 }
1316 .reviewers-title {
1316 .reviewers-title {
1317 width: 25%;
1317 width: 25%;
1318 min-width: 200px;
1318 min-width: 200px;
1319 }
1319 }
1320 .reviewers {
1320 .reviewers {
1321 width: 25%;
1321 width: 25%;
1322 min-width: 200px;
1322 min-width: 200px;
1323 }
1323 }
1324 .reviewers ul li {
1324 .reviewers ul li {
1325 position: relative;
1325 position: relative;
1326 width: 100%;
1326 width: 100%;
1327 padding-bottom: 8px;
1327 padding-bottom: 8px;
1328 }
1328 }
1329
1329
1330 .reviewer_entry {
1330 .reviewer_entry {
1331 min-height: 55px;
1331 min-height: 55px;
1332 }
1332 }
1333
1333
1334 .reviewers_member {
1334 .reviewers_member {
1335 width: 100%;
1335 width: 100%;
1336 overflow: auto;
1336 overflow: auto;
1337 }
1337 }
1338 .reviewer_reason {
1338 .reviewer_reason {
1339 padding-left: 20px;
1339 padding-left: 20px;
1340 line-height: 1.5em;
1340 line-height: 1.5em;
1341 }
1341 }
1342 .reviewer_status {
1342 .reviewer_status {
1343 display: inline-block;
1343 display: inline-block;
1344 vertical-align: top;
1344 vertical-align: top;
1345 width: 25px;
1345 width: 25px;
1346 min-width: 25px;
1346 min-width: 25px;
1347 height: 1.2em;
1347 height: 1.2em;
1348 margin-top: 3px;
1348 margin-top: 3px;
1349 line-height: 1em;
1349 line-height: 1em;
1350 }
1350 }
1351
1351
1352 .reviewer_name {
1352 .reviewer_name {
1353 display: inline-block;
1353 display: inline-block;
1354 max-width: 83%;
1354 max-width: 83%;
1355 padding-right: 20px;
1355 padding-right: 20px;
1356 vertical-align: middle;
1356 vertical-align: middle;
1357 line-height: 1;
1357 line-height: 1;
1358
1358
1359 .rc-user {
1359 .rc-user {
1360 min-width: 0;
1360 min-width: 0;
1361 margin: -2px 1em 0 0;
1361 margin: -2px 1em 0 0;
1362 }
1362 }
1363
1363
1364 .reviewer {
1364 .reviewer {
1365 float: left;
1365 float: left;
1366 }
1366 }
1367 }
1367 }
1368
1368
1369 .reviewer_member_mandatory {
1369 .reviewer_member_mandatory {
1370 position: absolute;
1370 position: absolute;
1371 left: 15px;
1371 left: 15px;
1372 top: 8px;
1372 top: 8px;
1373 width: 16px;
1373 width: 16px;
1374 font-size: 11px;
1374 font-size: 11px;
1375 margin: 0;
1375 margin: 0;
1376 padding: 0;
1376 padding: 0;
1377 color: black;
1377 color: black;
1378 }
1378 }
1379
1379
1380 .reviewer_member_mandatory_remove,
1380 .reviewer_member_mandatory_remove,
1381 .reviewer_member_remove {
1381 .reviewer_member_remove {
1382 position: absolute;
1382 position: absolute;
1383 right: 0;
1383 right: 0;
1384 top: 0;
1384 top: 0;
1385 width: 16px;
1385 width: 16px;
1386 margin-bottom: 10px;
1386 margin-bottom: 10px;
1387 padding: 0;
1387 padding: 0;
1388 color: black;
1388 color: black;
1389 }
1389 }
1390
1390
1391 .reviewer_member_mandatory_remove {
1391 .reviewer_member_mandatory_remove {
1392 color: @grey4;
1392 color: @grey4;
1393 }
1393 }
1394
1394
1395 .reviewer_member_status {
1395 .reviewer_member_status {
1396 margin-top: 5px;
1396 margin-top: 5px;
1397 }
1397 }
1398 .pr-summary #summary{
1398 .pr-summary #summary{
1399 width: 100%;
1399 width: 100%;
1400 }
1400 }
1401 .pr-summary .action_button:hover {
1401 .pr-summary .action_button:hover {
1402 border: 0;
1402 border: 0;
1403 cursor: pointer;
1403 cursor: pointer;
1404 }
1404 }
1405 .pr-details-title {
1405 .pr-details-title {
1406 padding-bottom: 8px;
1406 padding-bottom: 8px;
1407 border-bottom: @border-thickness solid @grey5;
1407 border-bottom: @border-thickness solid @grey5;
1408
1408
1409 .action_button.disabled {
1409 .action_button.disabled {
1410 color: @grey4;
1410 color: @grey4;
1411 cursor: inherit;
1411 cursor: inherit;
1412 }
1412 }
1413 .action_button {
1413 .action_button {
1414 color: @rcblue;
1414 color: @rcblue;
1415 }
1415 }
1416 }
1416 }
1417 .pr-details-content {
1417 .pr-details-content {
1418 margin-top: @textmargin;
1418 margin-top: @textmargin;
1419 margin-bottom: @textmargin;
1419 margin-bottom: @textmargin;
1420 }
1420 }
1421 .pr-description {
1422 white-space:pre-wrap;
1423 }
1424
1421
1425 .pr-reviewer-rules {
1422 .pr-reviewer-rules {
1426 padding: 10px 0px 20px 0px;
1423 padding: 10px 0px 20px 0px;
1427 }
1424 }
1428
1425
1429 .group_members {
1426 .group_members {
1430 margin-top: 0;
1427 margin-top: 0;
1431 padding: 0;
1428 padding: 0;
1432 list-style: outside none none;
1429 list-style: outside none none;
1433
1430
1434 img {
1431 img {
1435 height: @gravatar-size;
1432 height: @gravatar-size;
1436 width: @gravatar-size;
1433 width: @gravatar-size;
1437 margin-right: .5em;
1434 margin-right: .5em;
1438 margin-left: 3px;
1435 margin-left: 3px;
1439 }
1436 }
1440
1437
1441 .to-delete {
1438 .to-delete {
1442 .user {
1439 .user {
1443 text-decoration: line-through;
1440 text-decoration: line-through;
1444 }
1441 }
1445 }
1442 }
1446 }
1443 }
1447
1444
1448 .compare_view_commits_title {
1445 .compare_view_commits_title {
1449 .disabled {
1446 .disabled {
1450 cursor: inherit;
1447 cursor: inherit;
1451 &:hover{
1448 &:hover{
1452 background-color: inherit;
1449 background-color: inherit;
1453 color: inherit;
1450 color: inherit;
1454 }
1451 }
1455 }
1452 }
1456 }
1453 }
1457
1454
1458 .subtitle-compare {
1455 .subtitle-compare {
1459 margin: -15px 0px 0px 0px;
1456 margin: -15px 0px 0px 0px;
1460 }
1457 }
1461
1458
1462 .comments-summary-td {
1459 .comments-summary-td {
1463 border-top: 1px dashed @grey5;
1460 border-top: 1px dashed @grey5;
1464 }
1461 }
1465
1462
1466 // new entry in group_members
1463 // new entry in group_members
1467 .td-author-new-entry {
1464 .td-author-new-entry {
1468 background-color: rgba(red(@alert1), green(@alert1), blue(@alert1), 0.3);
1465 background-color: rgba(red(@alert1), green(@alert1), blue(@alert1), 0.3);
1469 }
1466 }
1470
1467
1471 .usergroup_member_remove {
1468 .usergroup_member_remove {
1472 width: 16px;
1469 width: 16px;
1473 margin-bottom: 10px;
1470 margin-bottom: 10px;
1474 padding: 0;
1471 padding: 0;
1475 color: black !important;
1472 color: black !important;
1476 cursor: pointer;
1473 cursor: pointer;
1477 }
1474 }
1478
1475
1479 .reviewer_ac .ac-input {
1476 .reviewer_ac .ac-input {
1480 width: 92%;
1477 width: 92%;
1481 margin-bottom: 1em;
1478 margin-bottom: 1em;
1482 }
1479 }
1483
1480
1484 .compare_view_commits tr{
1481 .compare_view_commits tr{
1485 height: 20px;
1482 height: 20px;
1486 }
1483 }
1487 .compare_view_commits td {
1484 .compare_view_commits td {
1488 vertical-align: top;
1485 vertical-align: top;
1489 padding-top: 10px;
1486 padding-top: 10px;
1490 }
1487 }
1491 .compare_view_commits .author {
1488 .compare_view_commits .author {
1492 margin-left: 5px;
1489 margin-left: 5px;
1493 }
1490 }
1494
1491
1495 .compare_view_commits {
1492 .compare_view_commits {
1496 .color-a {
1493 .color-a {
1497 color: @alert1;
1494 color: @alert1;
1498 }
1495 }
1499
1496
1500 .color-c {
1497 .color-c {
1501 color: @color3;
1498 color: @color3;
1502 }
1499 }
1503
1500
1504 .color-r {
1501 .color-r {
1505 color: @color5;
1502 color: @color5;
1506 }
1503 }
1507
1504
1508 .color-a-bg {
1505 .color-a-bg {
1509 background-color: @alert1;
1506 background-color: @alert1;
1510 }
1507 }
1511
1508
1512 .color-c-bg {
1509 .color-c-bg {
1513 background-color: @alert3;
1510 background-color: @alert3;
1514 }
1511 }
1515
1512
1516 .color-r-bg {
1513 .color-r-bg {
1517 background-color: @alert2;
1514 background-color: @alert2;
1518 }
1515 }
1519
1516
1520 .color-a-border {
1517 .color-a-border {
1521 border: 1px solid @alert1;
1518 border: 1px solid @alert1;
1522 }
1519 }
1523
1520
1524 .color-c-border {
1521 .color-c-border {
1525 border: 1px solid @alert3;
1522 border: 1px solid @alert3;
1526 }
1523 }
1527
1524
1528 .color-r-border {
1525 .color-r-border {
1529 border: 1px solid @alert2;
1526 border: 1px solid @alert2;
1530 }
1527 }
1531
1528
1532 .commit-change-indicator {
1529 .commit-change-indicator {
1533 width: 15px;
1530 width: 15px;
1534 height: 15px;
1531 height: 15px;
1535 position: relative;
1532 position: relative;
1536 left: 15px;
1533 left: 15px;
1537 }
1534 }
1538
1535
1539 .commit-change-content {
1536 .commit-change-content {
1540 text-align: center;
1537 text-align: center;
1541 vertical-align: middle;
1538 vertical-align: middle;
1542 line-height: 15px;
1539 line-height: 15px;
1543 }
1540 }
1544 }
1541 }
1545
1542
1546 .compare_view_filepath {
1543 .compare_view_filepath {
1547 color: @grey1;
1544 color: @grey1;
1548 }
1545 }
1549
1546
1550 .show_more {
1547 .show_more {
1551 display: inline-block;
1548 display: inline-block;
1552 position: relative;
1549 position: relative;
1553 vertical-align: middle;
1550 vertical-align: middle;
1554 width: 4px;
1551 width: 4px;
1555 height: @basefontsize;
1552 height: @basefontsize;
1556
1553
1557 &:after {
1554 &:after {
1558 content: "\00A0\25BE";
1555 content: "\00A0\25BE";
1559 display: inline-block;
1556 display: inline-block;
1560 width:10px;
1557 width:10px;
1561 line-height: 5px;
1558 line-height: 5px;
1562 font-size: 12px;
1559 font-size: 12px;
1563 cursor: pointer;
1560 cursor: pointer;
1564 }
1561 }
1565 }
1562 }
1566
1563
1567 .journal_more .show_more {
1564 .journal_more .show_more {
1568 display: inline;
1565 display: inline;
1569
1566
1570 &:after {
1567 &:after {
1571 content: none;
1568 content: none;
1572 }
1569 }
1573 }
1570 }
1574
1571
1575 .open .show_more:after,
1572 .open .show_more:after,
1576 .select2-dropdown-open .show_more:after {
1573 .select2-dropdown-open .show_more:after {
1577 .rotate(180deg);
1574 .rotate(180deg);
1578 margin-left: 4px;
1575 margin-left: 4px;
1579 }
1576 }
1580
1577
1581
1578
1582 .compare_view_commits .collapse_commit:after {
1579 .compare_view_commits .collapse_commit:after {
1583 cursor: pointer;
1580 cursor: pointer;
1584 content: "\00A0\25B4";
1581 content: "\00A0\25B4";
1585 margin-left: -3px;
1582 margin-left: -3px;
1586 font-size: 17px;
1583 font-size: 17px;
1587 color: @grey4;
1584 color: @grey4;
1588 }
1585 }
1589
1586
1590 .diff_links {
1587 .diff_links {
1591 margin-left: 8px;
1588 margin-left: 8px;
1592 }
1589 }
1593
1590
1594 div.ancestor {
1591 div.ancestor {
1595 margin: -30px 0px;
1592 margin: -30px 0px;
1596 }
1593 }
1597
1594
1598 .cs_icon_td input[type="checkbox"] {
1595 .cs_icon_td input[type="checkbox"] {
1599 display: none;
1596 display: none;
1600 }
1597 }
1601
1598
1602 .cs_icon_td .expand_file_icon:after {
1599 .cs_icon_td .expand_file_icon:after {
1603 cursor: pointer;
1600 cursor: pointer;
1604 content: "\00A0\25B6";
1601 content: "\00A0\25B6";
1605 font-size: 12px;
1602 font-size: 12px;
1606 color: @grey4;
1603 color: @grey4;
1607 }
1604 }
1608
1605
1609 .cs_icon_td .collapse_file_icon:after {
1606 .cs_icon_td .collapse_file_icon:after {
1610 cursor: pointer;
1607 cursor: pointer;
1611 content: "\00A0\25BC";
1608 content: "\00A0\25BC";
1612 font-size: 12px;
1609 font-size: 12px;
1613 color: @grey4;
1610 color: @grey4;
1614 }
1611 }
1615
1612
1616 /*new binary
1613 /*new binary
1617 NEW_FILENODE = 1
1614 NEW_FILENODE = 1
1618 DEL_FILENODE = 2
1615 DEL_FILENODE = 2
1619 MOD_FILENODE = 3
1616 MOD_FILENODE = 3
1620 RENAMED_FILENODE = 4
1617 RENAMED_FILENODE = 4
1621 COPIED_FILENODE = 5
1618 COPIED_FILENODE = 5
1622 CHMOD_FILENODE = 6
1619 CHMOD_FILENODE = 6
1623 BIN_FILENODE = 7
1620 BIN_FILENODE = 7
1624 */
1621 */
1625 .cs_files_expand {
1622 .cs_files_expand {
1626 font-size: @basefontsize + 5px;
1623 font-size: @basefontsize + 5px;
1627 line-height: 1.8em;
1624 line-height: 1.8em;
1628 float: right;
1625 float: right;
1629 }
1626 }
1630
1627
1631 .cs_files_expand span{
1628 .cs_files_expand span{
1632 color: @rcblue;
1629 color: @rcblue;
1633 cursor: pointer;
1630 cursor: pointer;
1634 }
1631 }
1635 .cs_files {
1632 .cs_files {
1636 clear: both;
1633 clear: both;
1637 padding-bottom: @padding;
1634 padding-bottom: @padding;
1638
1635
1639 .cur_cs {
1636 .cur_cs {
1640 margin: 10px 2px;
1637 margin: 10px 2px;
1641 font-weight: bold;
1638 font-weight: bold;
1642 }
1639 }
1643
1640
1644 .node {
1641 .node {
1645 float: left;
1642 float: left;
1646 }
1643 }
1647
1644
1648 .changes {
1645 .changes {
1649 float: right;
1646 float: right;
1650 color: white;
1647 color: white;
1651 font-size: @basefontsize - 4px;
1648 font-size: @basefontsize - 4px;
1652 margin-top: 4px;
1649 margin-top: 4px;
1653 opacity: 0.6;
1650 opacity: 0.6;
1654 filter: Alpha(opacity=60); /* IE8 and earlier */
1651 filter: Alpha(opacity=60); /* IE8 and earlier */
1655
1652
1656 .added {
1653 .added {
1657 background-color: @alert1;
1654 background-color: @alert1;
1658 float: left;
1655 float: left;
1659 text-align: center;
1656 text-align: center;
1660 }
1657 }
1661
1658
1662 .deleted {
1659 .deleted {
1663 background-color: @alert2;
1660 background-color: @alert2;
1664 float: left;
1661 float: left;
1665 text-align: center;
1662 text-align: center;
1666 }
1663 }
1667
1664
1668 .bin {
1665 .bin {
1669 background-color: @alert1;
1666 background-color: @alert1;
1670 text-align: center;
1667 text-align: center;
1671 }
1668 }
1672
1669
1673 /*new binary*/
1670 /*new binary*/
1674 .bin.bin1 {
1671 .bin.bin1 {
1675 background-color: @alert1;
1672 background-color: @alert1;
1676 text-align: center;
1673 text-align: center;
1677 }
1674 }
1678
1675
1679 /*deleted binary*/
1676 /*deleted binary*/
1680 .bin.bin2 {
1677 .bin.bin2 {
1681 background-color: @alert2;
1678 background-color: @alert2;
1682 text-align: center;
1679 text-align: center;
1683 }
1680 }
1684
1681
1685 /*mod binary*/
1682 /*mod binary*/
1686 .bin.bin3 {
1683 .bin.bin3 {
1687 background-color: @grey2;
1684 background-color: @grey2;
1688 text-align: center;
1685 text-align: center;
1689 }
1686 }
1690
1687
1691 /*rename file*/
1688 /*rename file*/
1692 .bin.bin4 {
1689 .bin.bin4 {
1693 background-color: @alert4;
1690 background-color: @alert4;
1694 text-align: center;
1691 text-align: center;
1695 }
1692 }
1696
1693
1697 /*copied file*/
1694 /*copied file*/
1698 .bin.bin5 {
1695 .bin.bin5 {
1699 background-color: @alert4;
1696 background-color: @alert4;
1700 text-align: center;
1697 text-align: center;
1701 }
1698 }
1702
1699
1703 /*chmod file*/
1700 /*chmod file*/
1704 .bin.bin6 {
1701 .bin.bin6 {
1705 background-color: @grey2;
1702 background-color: @grey2;
1706 text-align: center;
1703 text-align: center;
1707 }
1704 }
1708 }
1705 }
1709 }
1706 }
1710
1707
1711 .cs_files .cs_added, .cs_files .cs_A,
1708 .cs_files .cs_added, .cs_files .cs_A,
1712 .cs_files .cs_added, .cs_files .cs_M,
1709 .cs_files .cs_added, .cs_files .cs_M,
1713 .cs_files .cs_added, .cs_files .cs_D {
1710 .cs_files .cs_added, .cs_files .cs_D {
1714 height: 16px;
1711 height: 16px;
1715 padding-right: 10px;
1712 padding-right: 10px;
1716 margin-top: 7px;
1713 margin-top: 7px;
1717 text-align: left;
1714 text-align: left;
1718 }
1715 }
1719
1716
1720 .cs_icon_td {
1717 .cs_icon_td {
1721 min-width: 16px;
1718 min-width: 16px;
1722 width: 16px;
1719 width: 16px;
1723 }
1720 }
1724
1721
1725 .pull-request-merge {
1722 .pull-request-merge {
1726 border: 1px solid @grey5;
1723 border: 1px solid @grey5;
1727 padding: 10px 0px 20px;
1724 padding: 10px 0px 20px;
1728 margin-top: 10px;
1725 margin-top: 10px;
1729 margin-bottom: 20px;
1726 margin-bottom: 20px;
1730 }
1727 }
1731
1728
1732 .pull-request-merge ul {
1729 .pull-request-merge ul {
1733 padding: 0px 0px;
1730 padding: 0px 0px;
1734 }
1731 }
1735
1732
1736 .pull-request-merge li:before{
1733 .pull-request-merge li:before{
1737 content:none;
1734 content:none;
1738 }
1735 }
1739
1736
1740 .pull-request-merge .pull-request-wrap {
1737 .pull-request-merge .pull-request-wrap {
1741 height: auto;
1738 height: auto;
1742 padding: 0px 0px;
1739 padding: 0px 0px;
1743 text-align: right;
1740 text-align: right;
1744 }
1741 }
1745
1742
1746 .pull-request-merge span {
1743 .pull-request-merge span {
1747 margin-right: 5px;
1744 margin-right: 5px;
1748 }
1745 }
1749
1746
1750 .pull-request-merge-actions {
1747 .pull-request-merge-actions {
1751 min-height: 30px;
1748 min-height: 30px;
1752 padding: 0px 0px;
1749 padding: 0px 0px;
1753 }
1750 }
1754
1751
1755 .pull-request-merge-info {
1752 .pull-request-merge-info {
1756 padding: 0px 5px 5px 0px;
1753 padding: 0px 5px 5px 0px;
1757 }
1754 }
1758
1755
1759 .merge-status {
1756 .merge-status {
1760 margin-right: 5px;
1757 margin-right: 5px;
1761 }
1758 }
1762
1759
1763 .merge-message {
1760 .merge-message {
1764 font-size: 1.2em
1761 font-size: 1.2em
1765 }
1762 }
1766
1763
1767 .merge-message.success i,
1764 .merge-message.success i,
1768 .merge-icon.success i {
1765 .merge-icon.success i {
1769 color:@alert1;
1766 color:@alert1;
1770 }
1767 }
1771
1768
1772 .merge-message.warning i,
1769 .merge-message.warning i,
1773 .merge-icon.warning i {
1770 .merge-icon.warning i {
1774 color: @alert3;
1771 color: @alert3;
1775 }
1772 }
1776
1773
1777 .merge-message.error i,
1774 .merge-message.error i,
1778 .merge-icon.error i {
1775 .merge-icon.error i {
1779 color:@alert2;
1776 color:@alert2;
1780 }
1777 }
1781
1778
1782 .pr-versions {
1779 .pr-versions {
1783 font-size: 1.1em;
1780 font-size: 1.1em;
1784
1781
1785 table {
1782 table {
1786 padding: 0px 5px;
1783 padding: 0px 5px;
1787 }
1784 }
1788
1785
1789 td {
1786 td {
1790 line-height: 15px;
1787 line-height: 15px;
1791 }
1788 }
1792
1789
1793 .flag_status {
1790 .flag_status {
1794 margin: 0;
1791 margin: 0;
1795 }
1792 }
1796
1793
1797 .compare-radio-button {
1794 .compare-radio-button {
1798 position: relative;
1795 position: relative;
1799 top: -3px;
1796 top: -3px;
1800 }
1797 }
1801 }
1798 }
1802
1799
1803
1800
1804 #close_pull_request {
1801 #close_pull_request {
1805 margin-right: 0px;
1802 margin-right: 0px;
1806 }
1803 }
1807
1804
1808 .empty_data {
1805 .empty_data {
1809 color: @grey4;
1806 color: @grey4;
1810 }
1807 }
1811
1808
1812 #changeset_compare_view_content {
1809 #changeset_compare_view_content {
1813 margin-bottom: @space;
1810 margin-bottom: @space;
1814 clear: both;
1811 clear: both;
1815 width: 100%;
1812 width: 100%;
1816 box-sizing: border-box;
1813 box-sizing: border-box;
1817 .border-radius(@border-radius);
1814 .border-radius(@border-radius);
1818
1815
1819 .help-block {
1816 .help-block {
1820 margin: @padding 0;
1817 margin: @padding 0;
1821 color: @text-color;
1818 color: @text-color;
1822 &.pre-formatting {
1819 &.pre-formatting {
1823 white-space: pre;
1820 white-space: pre;
1824 }
1821 }
1825 }
1822 }
1826
1823
1827 .empty_data {
1824 .empty_data {
1828 margin: @padding 0;
1825 margin: @padding 0;
1829 }
1826 }
1830
1827
1831 .alert {
1828 .alert {
1832 margin-bottom: @space;
1829 margin-bottom: @space;
1833 }
1830 }
1834 }
1831 }
1835
1832
1836 .table_disp {
1833 .table_disp {
1837 .status {
1834 .status {
1838 width: auto;
1835 width: auto;
1839
1836
1840 .flag_status {
1837 .flag_status {
1841 float: left;
1838 float: left;
1842 }
1839 }
1843 }
1840 }
1844 }
1841 }
1845
1842
1846
1843
1847 .creation_in_progress {
1844 .creation_in_progress {
1848 color: @grey4
1845 color: @grey4
1849 }
1846 }
1850
1847
1851 .status_box_menu {
1848 .status_box_menu {
1852 margin: 0;
1849 margin: 0;
1853 }
1850 }
1854
1851
1855 .notification-table{
1852 .notification-table{
1856 margin-bottom: @space;
1853 margin-bottom: @space;
1857 display: table;
1854 display: table;
1858 width: 100%;
1855 width: 100%;
1859
1856
1860 .container{
1857 .container{
1861 display: table-row;
1858 display: table-row;
1862
1859
1863 .notification-header{
1860 .notification-header{
1864 border-bottom: @border-thickness solid @border-default-color;
1861 border-bottom: @border-thickness solid @border-default-color;
1865 }
1862 }
1866
1863
1867 .notification-subject{
1864 .notification-subject{
1868 display: table-cell;
1865 display: table-cell;
1869 }
1866 }
1870 }
1867 }
1871 }
1868 }
1872
1869
1873 // Notifications
1870 // Notifications
1874 .notification-header{
1871 .notification-header{
1875 display: table;
1872 display: table;
1876 width: 100%;
1873 width: 100%;
1877 padding: floor(@basefontsize/2) 0;
1874 padding: floor(@basefontsize/2) 0;
1878 line-height: 1em;
1875 line-height: 1em;
1879
1876
1880 .desc, .delete-notifications, .read-notifications{
1877 .desc, .delete-notifications, .read-notifications{
1881 display: table-cell;
1878 display: table-cell;
1882 text-align: left;
1879 text-align: left;
1883 }
1880 }
1884
1881
1885 .desc{
1882 .desc{
1886 width: 1163px;
1883 width: 1163px;
1887 }
1884 }
1888
1885
1889 .delete-notifications, .read-notifications{
1886 .delete-notifications, .read-notifications{
1890 width: 35px;
1887 width: 35px;
1891 min-width: 35px; //fixes when only one button is displayed
1888 min-width: 35px; //fixes when only one button is displayed
1892 }
1889 }
1893 }
1890 }
1894
1891
1895 .notification-body {
1892 .notification-body {
1896 .markdown-block,
1893 .markdown-block,
1897 .rst-block {
1894 .rst-block {
1898 padding: @padding 0;
1895 padding: @padding 0;
1899 }
1896 }
1900
1897
1901 .notification-subject {
1898 .notification-subject {
1902 padding: @textmargin 0;
1899 padding: @textmargin 0;
1903 border-bottom: @border-thickness solid @border-default-color;
1900 border-bottom: @border-thickness solid @border-default-color;
1904 }
1901 }
1905 }
1902 }
1906
1903
1907
1904
1908 .notifications_buttons{
1905 .notifications_buttons{
1909 float: right;
1906 float: right;
1910 }
1907 }
1911
1908
1912 #notification-status{
1909 #notification-status{
1913 display: inline;
1910 display: inline;
1914 }
1911 }
1915
1912
1916 // Repositories
1913 // Repositories
1917
1914
1918 #summary.fields{
1915 #summary.fields{
1919 display: table;
1916 display: table;
1920
1917
1921 .field{
1918 .field{
1922 display: table-row;
1919 display: table-row;
1923
1920
1924 .label-summary{
1921 .label-summary{
1925 display: table-cell;
1922 display: table-cell;
1926 min-width: @label-summary-minwidth;
1923 min-width: @label-summary-minwidth;
1927 padding-top: @padding/2;
1924 padding-top: @padding/2;
1928 padding-bottom: @padding/2;
1925 padding-bottom: @padding/2;
1929 padding-right: @padding/2;
1926 padding-right: @padding/2;
1930 }
1927 }
1931
1928
1932 .input{
1929 .input{
1933 display: table-cell;
1930 display: table-cell;
1934 padding: @padding/2;
1931 padding: @padding/2;
1935
1932
1936 input{
1933 input{
1937 min-width: 29em;
1934 min-width: 29em;
1938 padding: @padding/4;
1935 padding: @padding/4;
1939 }
1936 }
1940 }
1937 }
1941 .statistics, .downloads{
1938 .statistics, .downloads{
1942 .disabled{
1939 .disabled{
1943 color: @grey4;
1940 color: @grey4;
1944 }
1941 }
1945 }
1942 }
1946 }
1943 }
1947 }
1944 }
1948
1945
1949 #summary{
1946 #summary{
1950 width: 70%;
1947 width: 70%;
1951 }
1948 }
1952
1949
1953
1950
1954 // Journal
1951 // Journal
1955 .journal.title {
1952 .journal.title {
1956 h5 {
1953 h5 {
1957 float: left;
1954 float: left;
1958 margin: 0;
1955 margin: 0;
1959 width: 70%;
1956 width: 70%;
1960 }
1957 }
1961
1958
1962 ul {
1959 ul {
1963 float: right;
1960 float: right;
1964 display: inline-block;
1961 display: inline-block;
1965 margin: 0;
1962 margin: 0;
1966 width: 30%;
1963 width: 30%;
1967 text-align: right;
1964 text-align: right;
1968
1965
1969 li {
1966 li {
1970 display: inline;
1967 display: inline;
1971 font-size: @journal-fontsize;
1968 font-size: @journal-fontsize;
1972 line-height: 1em;
1969 line-height: 1em;
1973
1970
1974 &:before { content: none; }
1971 &:before { content: none; }
1975 }
1972 }
1976 }
1973 }
1977 }
1974 }
1978
1975
1979 .filterexample {
1976 .filterexample {
1980 position: absolute;
1977 position: absolute;
1981 top: 95px;
1978 top: 95px;
1982 left: @contentpadding;
1979 left: @contentpadding;
1983 color: @rcblue;
1980 color: @rcblue;
1984 font-size: 11px;
1981 font-size: 11px;
1985 font-family: @text-regular;
1982 font-family: @text-regular;
1986 cursor: help;
1983 cursor: help;
1987
1984
1988 &:hover {
1985 &:hover {
1989 color: @rcdarkblue;
1986 color: @rcdarkblue;
1990 }
1987 }
1991
1988
1992 @media (max-width:768px) {
1989 @media (max-width:768px) {
1993 position: relative;
1990 position: relative;
1994 top: auto;
1991 top: auto;
1995 left: auto;
1992 left: auto;
1996 display: block;
1993 display: block;
1997 }
1994 }
1998 }
1995 }
1999
1996
2000
1997
2001 #journal{
1998 #journal{
2002 margin-bottom: @space;
1999 margin-bottom: @space;
2003
2000
2004 .journal_day{
2001 .journal_day{
2005 margin-bottom: @textmargin/2;
2002 margin-bottom: @textmargin/2;
2006 padding-bottom: @textmargin/2;
2003 padding-bottom: @textmargin/2;
2007 font-size: @journal-fontsize;
2004 font-size: @journal-fontsize;
2008 border-bottom: @border-thickness solid @border-default-color;
2005 border-bottom: @border-thickness solid @border-default-color;
2009 }
2006 }
2010
2007
2011 .journal_container{
2008 .journal_container{
2012 margin-bottom: @space;
2009 margin-bottom: @space;
2013
2010
2014 .journal_user{
2011 .journal_user{
2015 display: inline-block;
2012 display: inline-block;
2016 }
2013 }
2017 .journal_action_container{
2014 .journal_action_container{
2018 display: block;
2015 display: block;
2019 margin-top: @textmargin;
2016 margin-top: @textmargin;
2020
2017
2021 div{
2018 div{
2022 display: inline;
2019 display: inline;
2023 }
2020 }
2024
2021
2025 div.journal_action_params{
2022 div.journal_action_params{
2026 display: block;
2023 display: block;
2027 }
2024 }
2028
2025
2029 div.journal_repo:after{
2026 div.journal_repo:after{
2030 content: "\A";
2027 content: "\A";
2031 white-space: pre;
2028 white-space: pre;
2032 }
2029 }
2033
2030
2034 div.date{
2031 div.date{
2035 display: block;
2032 display: block;
2036 margin-bottom: @textmargin;
2033 margin-bottom: @textmargin;
2037 }
2034 }
2038 }
2035 }
2039 }
2036 }
2040 }
2037 }
2041
2038
2042 // Files
2039 // Files
2043 .edit-file-title {
2040 .edit-file-title {
2044 border-bottom: @border-thickness solid @border-default-color;
2041 border-bottom: @border-thickness solid @border-default-color;
2045
2042
2046 .breadcrumbs {
2043 .breadcrumbs {
2047 margin-bottom: 0;
2044 margin-bottom: 0;
2048 }
2045 }
2049 }
2046 }
2050
2047
2051 .edit-file-fieldset {
2048 .edit-file-fieldset {
2052 margin-top: @sidebarpadding;
2049 margin-top: @sidebarpadding;
2053
2050
2054 .fieldset {
2051 .fieldset {
2055 .left-label {
2052 .left-label {
2056 width: 13%;
2053 width: 13%;
2057 }
2054 }
2058 .right-content {
2055 .right-content {
2059 width: 87%;
2056 width: 87%;
2060 max-width: 100%;
2057 max-width: 100%;
2061 }
2058 }
2062 .filename-label {
2059 .filename-label {
2063 margin-top: 13px;
2060 margin-top: 13px;
2064 }
2061 }
2065 .commit-message-label {
2062 .commit-message-label {
2066 margin-top: 4px;
2063 margin-top: 4px;
2067 }
2064 }
2068 .file-upload-input {
2065 .file-upload-input {
2069 input {
2066 input {
2070 display: none;
2067 display: none;
2071 }
2068 }
2072 margin-top: 10px;
2069 margin-top: 10px;
2073 }
2070 }
2074 .file-upload-label {
2071 .file-upload-label {
2075 margin-top: 10px;
2072 margin-top: 10px;
2076 }
2073 }
2077 p {
2074 p {
2078 margin-top: 5px;
2075 margin-top: 5px;
2079 }
2076 }
2080
2077
2081 }
2078 }
2082 .custom-path-link {
2079 .custom-path-link {
2083 margin-left: 5px;
2080 margin-left: 5px;
2084 }
2081 }
2085 #commit {
2082 #commit {
2086 resize: vertical;
2083 resize: vertical;
2087 }
2084 }
2088 }
2085 }
2089
2086
2090 .delete-file-preview {
2087 .delete-file-preview {
2091 max-height: 250px;
2088 max-height: 250px;
2092 }
2089 }
2093
2090
2094 .new-file,
2091 .new-file,
2095 #filter_activate,
2092 #filter_activate,
2096 #filter_deactivate {
2093 #filter_deactivate {
2097 float: left;
2094 float: left;
2098 margin: 0 0 0 15px;
2095 margin: 0 0 0 15px;
2099 }
2096 }
2100
2097
2101 h3.files_location{
2098 h3.files_location{
2102 line-height: 2.4em;
2099 line-height: 2.4em;
2103 }
2100 }
2104
2101
2105 .browser-nav {
2102 .browser-nav {
2106 display: table;
2103 display: table;
2107 margin-bottom: @space;
2104 margin-bottom: @space;
2108
2105
2109
2106
2110 .info_box {
2107 .info_box {
2111 display: inline-table;
2108 display: inline-table;
2112 height: 2.5em;
2109 height: 2.5em;
2113
2110
2114 .browser-cur-rev, .info_box_elem {
2111 .browser-cur-rev, .info_box_elem {
2115 display: table-cell;
2112 display: table-cell;
2116 vertical-align: middle;
2113 vertical-align: middle;
2117 }
2114 }
2118
2115
2119 .info_box_elem {
2116 .info_box_elem {
2120 border-top: @border-thickness solid @rcblue;
2117 border-top: @border-thickness solid @rcblue;
2121 border-bottom: @border-thickness solid @rcblue;
2118 border-bottom: @border-thickness solid @rcblue;
2122
2119
2123 #at_rev, a {
2120 #at_rev, a {
2124 padding: 0.6em 0.9em;
2121 padding: 0.6em 0.9em;
2125 margin: 0;
2122 margin: 0;
2126 .box-shadow(none);
2123 .box-shadow(none);
2127 border: 0;
2124 border: 0;
2128 height: 12px;
2125 height: 12px;
2129 }
2126 }
2130
2127
2131 input#at_rev {
2128 input#at_rev {
2132 max-width: 50px;
2129 max-width: 50px;
2133 text-align: right;
2130 text-align: right;
2134 }
2131 }
2135
2132
2136 &.previous {
2133 &.previous {
2137 border: @border-thickness solid @rcblue;
2134 border: @border-thickness solid @rcblue;
2138 .disabled {
2135 .disabled {
2139 color: @grey4;
2136 color: @grey4;
2140 cursor: not-allowed;
2137 cursor: not-allowed;
2141 }
2138 }
2142 }
2139 }
2143
2140
2144 &.next {
2141 &.next {
2145 border: @border-thickness solid @rcblue;
2142 border: @border-thickness solid @rcblue;
2146 .disabled {
2143 .disabled {
2147 color: @grey4;
2144 color: @grey4;
2148 cursor: not-allowed;
2145 cursor: not-allowed;
2149 }
2146 }
2150 }
2147 }
2151 }
2148 }
2152
2149
2153 .browser-cur-rev {
2150 .browser-cur-rev {
2154
2151
2155 span{
2152 span{
2156 margin: 0;
2153 margin: 0;
2157 color: @rcblue;
2154 color: @rcblue;
2158 height: 12px;
2155 height: 12px;
2159 display: inline-block;
2156 display: inline-block;
2160 padding: 0.7em 1em ;
2157 padding: 0.7em 1em ;
2161 border: @border-thickness solid @rcblue;
2158 border: @border-thickness solid @rcblue;
2162 margin-right: @padding;
2159 margin-right: @padding;
2163 }
2160 }
2164 }
2161 }
2165 }
2162 }
2166
2163
2167 .search_activate {
2164 .search_activate {
2168 display: table-cell;
2165 display: table-cell;
2169 vertical-align: middle;
2166 vertical-align: middle;
2170
2167
2171 input, label{
2168 input, label{
2172 margin: 0;
2169 margin: 0;
2173 padding: 0;
2170 padding: 0;
2174 }
2171 }
2175
2172
2176 input{
2173 input{
2177 margin-left: @textmargin;
2174 margin-left: @textmargin;
2178 }
2175 }
2179
2176
2180 }
2177 }
2181 }
2178 }
2182
2179
2183 .browser-cur-rev{
2180 .browser-cur-rev{
2184 margin-bottom: @textmargin;
2181 margin-bottom: @textmargin;
2185 }
2182 }
2186
2183
2187 #node_filter_box_loading{
2184 #node_filter_box_loading{
2188 .info_text;
2185 .info_text;
2189 }
2186 }
2190
2187
2191 .browser-search {
2188 .browser-search {
2192 margin: -25px 0px 5px 0px;
2189 margin: -25px 0px 5px 0px;
2193 }
2190 }
2194
2191
2195 .node-filter {
2192 .node-filter {
2196 font-size: @repo-title-fontsize;
2193 font-size: @repo-title-fontsize;
2197 padding: 4px 0px 0px 0px;
2194 padding: 4px 0px 0px 0px;
2198
2195
2199 .node-filter-path {
2196 .node-filter-path {
2200 float: left;
2197 float: left;
2201 color: @grey4;
2198 color: @grey4;
2202 }
2199 }
2203 .node-filter-input {
2200 .node-filter-input {
2204 float: left;
2201 float: left;
2205 margin: -2px 0px 0px 2px;
2202 margin: -2px 0px 0px 2px;
2206 input {
2203 input {
2207 padding: 2px;
2204 padding: 2px;
2208 border: none;
2205 border: none;
2209 font-size: @repo-title-fontsize;
2206 font-size: @repo-title-fontsize;
2210 }
2207 }
2211 }
2208 }
2212 }
2209 }
2213
2210
2214
2211
2215 .browser-result{
2212 .browser-result{
2216 td a{
2213 td a{
2217 margin-left: 0.5em;
2214 margin-left: 0.5em;
2218 display: inline-block;
2215 display: inline-block;
2219
2216
2220 em{
2217 em{
2221 font-family: @text-bold;
2218 font-family: @text-bold;
2222 }
2219 }
2223 }
2220 }
2224 }
2221 }
2225
2222
2226 .browser-highlight{
2223 .browser-highlight{
2227 background-color: @grey5-alpha;
2224 background-color: @grey5-alpha;
2228 }
2225 }
2229
2226
2230
2227
2231 // Search
2228 // Search
2232
2229
2233 .search-form{
2230 .search-form{
2234 #q {
2231 #q {
2235 width: @search-form-width;
2232 width: @search-form-width;
2236 }
2233 }
2237 .fields{
2234 .fields{
2238 margin: 0 0 @space;
2235 margin: 0 0 @space;
2239 }
2236 }
2240
2237
2241 label{
2238 label{
2242 display: inline-block;
2239 display: inline-block;
2243 margin-right: @textmargin;
2240 margin-right: @textmargin;
2244 padding-top: 0.25em;
2241 padding-top: 0.25em;
2245 }
2242 }
2246
2243
2247
2244
2248 .results{
2245 .results{
2249 clear: both;
2246 clear: both;
2250 margin: 0 0 @padding;
2247 margin: 0 0 @padding;
2251 }
2248 }
2252 }
2249 }
2253
2250
2254 div.search-feedback-items {
2251 div.search-feedback-items {
2255 display: inline-block;
2252 display: inline-block;
2256 padding:0px 0px 0px 96px;
2253 padding:0px 0px 0px 96px;
2257 }
2254 }
2258
2255
2259 div.search-code-body {
2256 div.search-code-body {
2260 background-color: #ffffff; padding: 5px 0 5px 10px;
2257 background-color: #ffffff; padding: 5px 0 5px 10px;
2261 pre {
2258 pre {
2262 .match { background-color: #faffa6;}
2259 .match { background-color: #faffa6;}
2263 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
2260 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
2264 }
2261 }
2265 }
2262 }
2266
2263
2267 .expand_commit.search {
2264 .expand_commit.search {
2268 .show_more.open {
2265 .show_more.open {
2269 height: auto;
2266 height: auto;
2270 max-height: none;
2267 max-height: none;
2271 }
2268 }
2272 }
2269 }
2273
2270
2274 .search-results {
2271 .search-results {
2275
2272
2276 h2 {
2273 h2 {
2277 margin-bottom: 0;
2274 margin-bottom: 0;
2278 }
2275 }
2279 .codeblock {
2276 .codeblock {
2280 border: none;
2277 border: none;
2281 background: transparent;
2278 background: transparent;
2282 }
2279 }
2283
2280
2284 .codeblock-header {
2281 .codeblock-header {
2285 border: none;
2282 border: none;
2286 background: transparent;
2283 background: transparent;
2287 }
2284 }
2288
2285
2289 .code-body {
2286 .code-body {
2290 border: @border-thickness solid @border-default-color;
2287 border: @border-thickness solid @border-default-color;
2291 .border-radius(@border-radius);
2288 .border-radius(@border-radius);
2292 }
2289 }
2293
2290
2294 .td-commit {
2291 .td-commit {
2295 &:extend(pre);
2292 &:extend(pre);
2296 border-bottom: @border-thickness solid @border-default-color;
2293 border-bottom: @border-thickness solid @border-default-color;
2297 }
2294 }
2298
2295
2299 .message {
2296 .message {
2300 height: auto;
2297 height: auto;
2301 max-width: 350px;
2298 max-width: 350px;
2302 white-space: normal;
2299 white-space: normal;
2303 text-overflow: initial;
2300 text-overflow: initial;
2304 overflow: visible;
2301 overflow: visible;
2305
2302
2306 .match { background-color: #faffa6;}
2303 .match { background-color: #faffa6;}
2307 .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; }
2304 .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; }
2308 }
2305 }
2309
2306
2310 }
2307 }
2311
2308
2312 table.rctable td.td-search-results div {
2309 table.rctable td.td-search-results div {
2313 max-width: 100%;
2310 max-width: 100%;
2314 }
2311 }
2315
2312
2316 #tip-box, .tip-box{
2313 #tip-box, .tip-box{
2317 padding: @menupadding/2;
2314 padding: @menupadding/2;
2318 display: block;
2315 display: block;
2319 border: @border-thickness solid @border-highlight-color;
2316 border: @border-thickness solid @border-highlight-color;
2320 .border-radius(@border-radius);
2317 .border-radius(@border-radius);
2321 background-color: white;
2318 background-color: white;
2322 z-index: 99;
2319 z-index: 99;
2323 white-space: pre-wrap;
2320 white-space: pre-wrap;
2324 }
2321 }
2325
2322
2326 #linktt {
2323 #linktt {
2327 width: 79px;
2324 width: 79px;
2328 }
2325 }
2329
2326
2330 #help_kb .modal-content{
2327 #help_kb .modal-content{
2331 max-width: 750px;
2328 max-width: 750px;
2332 margin: 10% auto;
2329 margin: 10% auto;
2333
2330
2334 table{
2331 table{
2335 td,th{
2332 td,th{
2336 border-bottom: none;
2333 border-bottom: none;
2337 line-height: 2.5em;
2334 line-height: 2.5em;
2338 }
2335 }
2339 th{
2336 th{
2340 padding-bottom: @textmargin/2;
2337 padding-bottom: @textmargin/2;
2341 }
2338 }
2342 td.keys{
2339 td.keys{
2343 text-align: center;
2340 text-align: center;
2344 }
2341 }
2345 }
2342 }
2346
2343
2347 .block-left{
2344 .block-left{
2348 width: 45%;
2345 width: 45%;
2349 margin-right: 5%;
2346 margin-right: 5%;
2350 }
2347 }
2351 .modal-footer{
2348 .modal-footer{
2352 clear: both;
2349 clear: both;
2353 }
2350 }
2354 .key.tag{
2351 .key.tag{
2355 padding: 0.5em;
2352 padding: 0.5em;
2356 background-color: @rcblue;
2353 background-color: @rcblue;
2357 color: white;
2354 color: white;
2358 border-color: @rcblue;
2355 border-color: @rcblue;
2359 .box-shadow(none);
2356 .box-shadow(none);
2360 }
2357 }
2361 }
2358 }
2362
2359
2363
2360
2364
2361
2365 //--- IMPORTS FOR REFACTORED STYLES ------------------//
2362 //--- IMPORTS FOR REFACTORED STYLES ------------------//
2366
2363
2367 @import 'statistics-graph';
2364 @import 'statistics-graph';
2368 @import 'tables';
2365 @import 'tables';
2369 @import 'forms';
2366 @import 'forms';
2370 @import 'diff';
2367 @import 'diff';
2371 @import 'summary';
2368 @import 'summary';
2372 @import 'navigation';
2369 @import 'navigation';
2373
2370
2374 //--- SHOW/HIDE SECTIONS --//
2371 //--- SHOW/HIDE SECTIONS --//
2375
2372
2376 .btn-collapse {
2373 .btn-collapse {
2377 float: right;
2374 float: right;
2378 text-align: right;
2375 text-align: right;
2379 font-family: @text-light;
2376 font-family: @text-light;
2380 font-size: @basefontsize;
2377 font-size: @basefontsize;
2381 cursor: pointer;
2378 cursor: pointer;
2382 border: none;
2379 border: none;
2383 color: @rcblue;
2380 color: @rcblue;
2384 }
2381 }
2385
2382
2386 table.rctable,
2383 table.rctable,
2387 table.dataTable {
2384 table.dataTable {
2388 .btn-collapse {
2385 .btn-collapse {
2389 float: right;
2386 float: right;
2390 text-align: right;
2387 text-align: right;
2391 }
2388 }
2392 }
2389 }
2393
2390
2394
2391
2395 // TODO: johbo: Fix for IE10, this avoids that we see a border
2392 // TODO: johbo: Fix for IE10, this avoids that we see a border
2396 // and padding around checkboxes and radio boxes. Move to the right place,
2393 // and padding around checkboxes and radio boxes. Move to the right place,
2397 // or better: Remove this once we did the form refactoring.
2394 // or better: Remove this once we did the form refactoring.
2398 input[type=checkbox],
2395 input[type=checkbox],
2399 input[type=radio] {
2396 input[type=radio] {
2400 padding: 0;
2397 padding: 0;
2401 border: none;
2398 border: none;
2402 }
2399 }
2403
2400
2404 .toggle-ajax-spinner{
2401 .toggle-ajax-spinner{
2405 height: 16px;
2402 height: 16px;
2406 width: 16px;
2403 width: 16px;
2407 }
2404 }
2405
2406
2407 .markup-form .clearfix {
2408 .border-radius(@border-radius);
2409 margin: 0px;
2410 }
2411
2412 .markup-form-area {
2413 padding: 8px 12px;
2414 border: 1px solid @grey4;
2415 .border-radius(@border-radius);
2416 }
2417
2418 .markup-form-area-header .nav-links {
2419 display: flex;
2420 flex-flow: row wrap;
2421 -webkit-flex-flow: row wrap;
2422 width: 100%;
2423 }
2424
2425 .markup-form-area-footer {
2426 display: flex;
2427 }
2428
2429 .markup-form-area-footer .toolbar {
2430
2431 }
2432
2433 // markup Form
2434 div.markup-form {
2435 margin-top: 20px;
2436 }
2437
2438 .markup-form strong {
2439 display: block;
2440 margin-bottom: 15px;
2441 }
2442
2443 .markup-form textarea {
2444 width: 100%;
2445 height: 100px;
2446 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
2447 }
2448
2449 form.markup-form {
2450 margin-top: 10px;
2451 margin-left: 10px;
2452 }
2453
2454 .markup-form .comment-block-ta,
2455 .markup-form .preview-box {
2456 .border-radius(@border-radius);
2457 .box-sizing(border-box);
2458 background-color: white;
2459 }
2460
2461 .markup-form .preview-box.unloaded {
2462 height: 50px;
2463 text-align: center;
2464 padding: 20px;
2465 background-color: white;
2466 }
@@ -1,321 +1,325 b''
1
1
2 /******************************************************************************
2 /******************************************************************************
3 * *
3 * *
4 * DO NOT CHANGE THIS FILE MANUALLY *
4 * DO NOT CHANGE THIS FILE MANUALLY *
5 * *
5 * *
6 * *
6 * *
7 * This file is automatically generated when the app starts up with *
7 * This file is automatically generated when the app starts up with *
8 * generate_js_files = true *
8 * generate_js_files = true *
9 * *
9 * *
10 * To add a route here pass jsroute=True to the route definition in the app *
10 * To add a route here pass jsroute=True to the route definition in the app *
11 * *
11 * *
12 ******************************************************************************/
12 ******************************************************************************/
13 function registerRCRoutes() {
13 function registerRCRoutes() {
14 // routes registration
14 // routes registration
15 pyroutes.register('favicon', '/favicon.ico', []);
15 pyroutes.register('favicon', '/favicon.ico', []);
16 pyroutes.register('robots', '/robots.txt', []);
16 pyroutes.register('robots', '/robots.txt', []);
17 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
17 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
18 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
18 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
19 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
19 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
20 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
20 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
21 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
21 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
22 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
22 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
23 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/_settings/integrations', ['repo_group_name']);
23 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/_settings/integrations', ['repo_group_name']);
24 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/_settings/integrations/new', ['repo_group_name']);
24 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/_settings/integrations/new', ['repo_group_name']);
25 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/_settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
25 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/_settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
26 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/_settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
26 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/_settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
27 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/_settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
27 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/_settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
28 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
28 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
29 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
29 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
30 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
30 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
31 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
31 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
32 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
32 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
33 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
33 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
34 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
34 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
35 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
35 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
36 pyroutes.register('ops_ping_legacy', '/_admin/ping', []);
36 pyroutes.register('ops_ping_legacy', '/_admin/ping', []);
37 pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []);
37 pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []);
38 pyroutes.register('admin_home', '/_admin', []);
38 pyroutes.register('admin_home', '/_admin', []);
39 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
39 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
40 pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']);
40 pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']);
41 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
41 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
42 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
42 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
43 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
43 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
44 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
44 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
45 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
45 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
46 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
46 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
47 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
47 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
48 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
48 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
49 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
49 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
50 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
50 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
51 pyroutes.register('admin_settings_process_management_data', '/_admin/settings/process_management/data', []);
51 pyroutes.register('admin_settings_process_management_data', '/_admin/settings/process_management/data', []);
52 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
52 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
53 pyroutes.register('admin_settings_process_management_master_signal', '/_admin/settings/process_management/master_signal', []);
53 pyroutes.register('admin_settings_process_management_master_signal', '/_admin/settings/process_management/master_signal', []);
54 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
54 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
55 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
55 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
56 pyroutes.register('admin_settings', '/_admin/settings', []);
56 pyroutes.register('admin_settings', '/_admin/settings', []);
57 pyroutes.register('admin_settings_update', '/_admin/settings/update', []);
57 pyroutes.register('admin_settings_update', '/_admin/settings/update', []);
58 pyroutes.register('admin_settings_global', '/_admin/settings/global', []);
58 pyroutes.register('admin_settings_global', '/_admin/settings/global', []);
59 pyroutes.register('admin_settings_global_update', '/_admin/settings/global/update', []);
59 pyroutes.register('admin_settings_global_update', '/_admin/settings/global/update', []);
60 pyroutes.register('admin_settings_vcs', '/_admin/settings/vcs', []);
60 pyroutes.register('admin_settings_vcs', '/_admin/settings/vcs', []);
61 pyroutes.register('admin_settings_vcs_update', '/_admin/settings/vcs/update', []);
61 pyroutes.register('admin_settings_vcs_update', '/_admin/settings/vcs/update', []);
62 pyroutes.register('admin_settings_vcs_svn_pattern_delete', '/_admin/settings/vcs/svn_pattern_delete', []);
62 pyroutes.register('admin_settings_vcs_svn_pattern_delete', '/_admin/settings/vcs/svn_pattern_delete', []);
63 pyroutes.register('admin_settings_mapping', '/_admin/settings/mapping', []);
63 pyroutes.register('admin_settings_mapping', '/_admin/settings/mapping', []);
64 pyroutes.register('admin_settings_mapping_update', '/_admin/settings/mapping/update', []);
64 pyroutes.register('admin_settings_mapping_update', '/_admin/settings/mapping/update', []);
65 pyroutes.register('admin_settings_visual', '/_admin/settings/visual', []);
65 pyroutes.register('admin_settings_visual', '/_admin/settings/visual', []);
66 pyroutes.register('admin_settings_visual_update', '/_admin/settings/visual/update', []);
66 pyroutes.register('admin_settings_visual_update', '/_admin/settings/visual/update', []);
67 pyroutes.register('admin_settings_issuetracker', '/_admin/settings/issue-tracker', []);
67 pyroutes.register('admin_settings_issuetracker', '/_admin/settings/issue-tracker', []);
68 pyroutes.register('admin_settings_issuetracker_update', '/_admin/settings/issue-tracker/update', []);
68 pyroutes.register('admin_settings_issuetracker_update', '/_admin/settings/issue-tracker/update', []);
69 pyroutes.register('admin_settings_issuetracker_test', '/_admin/settings/issue-tracker/test', []);
69 pyroutes.register('admin_settings_issuetracker_test', '/_admin/settings/issue-tracker/test', []);
70 pyroutes.register('admin_settings_issuetracker_delete', '/_admin/settings/issue-tracker/delete', []);
70 pyroutes.register('admin_settings_issuetracker_delete', '/_admin/settings/issue-tracker/delete', []);
71 pyroutes.register('admin_settings_email', '/_admin/settings/email', []);
71 pyroutes.register('admin_settings_email', '/_admin/settings/email', []);
72 pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []);
72 pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []);
73 pyroutes.register('admin_settings_hooks', '/_admin/settings/hooks', []);
73 pyroutes.register('admin_settings_hooks', '/_admin/settings/hooks', []);
74 pyroutes.register('admin_settings_hooks_update', '/_admin/settings/hooks/update', []);
74 pyroutes.register('admin_settings_hooks_update', '/_admin/settings/hooks/update', []);
75 pyroutes.register('admin_settings_hooks_delete', '/_admin/settings/hooks/delete', []);
75 pyroutes.register('admin_settings_hooks_delete', '/_admin/settings/hooks/delete', []);
76 pyroutes.register('admin_settings_search', '/_admin/settings/search', []);
76 pyroutes.register('admin_settings_search', '/_admin/settings/search', []);
77 pyroutes.register('admin_settings_labs', '/_admin/settings/labs', []);
77 pyroutes.register('admin_settings_labs', '/_admin/settings/labs', []);
78 pyroutes.register('admin_settings_labs_update', '/_admin/settings/labs/update', []);
78 pyroutes.register('admin_settings_labs_update', '/_admin/settings/labs/update', []);
79 pyroutes.register('admin_settings_automation', '/_admin/_admin/settings/automation', []);
79 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
80 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
80 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
81 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
81 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
82 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
82 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
83 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
83 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
84 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
84 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
85 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
85 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
86 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
86 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
87 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
87 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
88 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
88 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
89 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
89 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
90 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
90 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
91 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
91 pyroutes.register('users', '/_admin/users', []);
92 pyroutes.register('users', '/_admin/users', []);
92 pyroutes.register('users_data', '/_admin/users_data', []);
93 pyroutes.register('users_data', '/_admin/users_data', []);
93 pyroutes.register('users_create', '/_admin/users/create', []);
94 pyroutes.register('users_create', '/_admin/users/create', []);
94 pyroutes.register('users_new', '/_admin/users/new', []);
95 pyroutes.register('users_new', '/_admin/users/new', []);
95 pyroutes.register('user_edit', '/_admin/users/%(user_id)s/edit', ['user_id']);
96 pyroutes.register('user_edit', '/_admin/users/%(user_id)s/edit', ['user_id']);
96 pyroutes.register('user_edit_advanced', '/_admin/users/%(user_id)s/edit/advanced', ['user_id']);
97 pyroutes.register('user_edit_advanced', '/_admin/users/%(user_id)s/edit/advanced', ['user_id']);
97 pyroutes.register('user_edit_global_perms', '/_admin/users/%(user_id)s/edit/global_permissions', ['user_id']);
98 pyroutes.register('user_edit_global_perms', '/_admin/users/%(user_id)s/edit/global_permissions', ['user_id']);
98 pyroutes.register('user_edit_global_perms_update', '/_admin/users/%(user_id)s/edit/global_permissions/update', ['user_id']);
99 pyroutes.register('user_edit_global_perms_update', '/_admin/users/%(user_id)s/edit/global_permissions/update', ['user_id']);
99 pyroutes.register('user_update', '/_admin/users/%(user_id)s/update', ['user_id']);
100 pyroutes.register('user_update', '/_admin/users/%(user_id)s/update', ['user_id']);
100 pyroutes.register('user_delete', '/_admin/users/%(user_id)s/delete', ['user_id']);
101 pyroutes.register('user_delete', '/_admin/users/%(user_id)s/delete', ['user_id']);
101 pyroutes.register('user_force_password_reset', '/_admin/users/%(user_id)s/password_reset', ['user_id']);
102 pyroutes.register('user_force_password_reset', '/_admin/users/%(user_id)s/password_reset', ['user_id']);
102 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
103 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
103 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
104 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
104 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
105 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
105 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
106 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
106 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
107 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
107 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
108 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
108 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
109 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
109 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
110 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
110 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
111 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
111 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
112 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
112 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
113 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
113 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
114 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
114 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
115 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
115 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
116 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
116 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
117 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
117 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
118 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
118 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
119 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
119 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
120 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
120 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
121 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
121 pyroutes.register('user_groups', '/_admin/user_groups', []);
122 pyroutes.register('user_groups', '/_admin/user_groups', []);
122 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
123 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
123 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
124 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
124 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
125 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
125 pyroutes.register('repos', '/_admin/repos', []);
126 pyroutes.register('repos', '/_admin/repos', []);
126 pyroutes.register('repo_new', '/_admin/repos/new', []);
127 pyroutes.register('repo_new', '/_admin/repos/new', []);
127 pyroutes.register('repo_create', '/_admin/repos/create', []);
128 pyroutes.register('repo_create', '/_admin/repos/create', []);
128 pyroutes.register('repo_groups', '/_admin/repo_groups', []);
129 pyroutes.register('repo_groups', '/_admin/repo_groups', []);
129 pyroutes.register('repo_group_new', '/_admin/repo_group/new', []);
130 pyroutes.register('repo_group_new', '/_admin/repo_group/new', []);
130 pyroutes.register('repo_group_create', '/_admin/repo_group/create', []);
131 pyroutes.register('repo_group_create', '/_admin/repo_group/create', []);
131 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
132 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
132 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
133 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
133 pyroutes.register('channelstream_proxy', '/_channelstream', []);
134 pyroutes.register('channelstream_proxy', '/_channelstream', []);
134 pyroutes.register('login', '/_admin/login', []);
135 pyroutes.register('login', '/_admin/login', []);
135 pyroutes.register('logout', '/_admin/logout', []);
136 pyroutes.register('logout', '/_admin/logout', []);
136 pyroutes.register('register', '/_admin/register', []);
137 pyroutes.register('register', '/_admin/register', []);
137 pyroutes.register('reset_password', '/_admin/password_reset', []);
138 pyroutes.register('reset_password', '/_admin/password_reset', []);
138 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
139 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
139 pyroutes.register('home', '/', []);
140 pyroutes.register('home', '/', []);
140 pyroutes.register('user_autocomplete_data', '/_users', []);
141 pyroutes.register('user_autocomplete_data', '/_users', []);
141 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
142 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
142 pyroutes.register('repo_list_data', '/_repos', []);
143 pyroutes.register('repo_list_data', '/_repos', []);
143 pyroutes.register('goto_switcher_data', '/_goto_data', []);
144 pyroutes.register('goto_switcher_data', '/_goto_data', []);
145 pyroutes.register('markup_preview', '/_markup_preview', []);
144 pyroutes.register('journal', '/_admin/journal', []);
146 pyroutes.register('journal', '/_admin/journal', []);
145 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
147 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
146 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
148 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
147 pyroutes.register('journal_public', '/_admin/public_journal', []);
149 pyroutes.register('journal_public', '/_admin/public_journal', []);
148 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
150 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
149 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
151 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
150 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
152 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
151 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
153 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
152 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
154 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
153 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
155 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
154 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
156 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
155 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
157 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
156 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
158 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
157 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
159 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
158 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
160 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
159 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
161 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
160 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
162 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
161 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
163 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
162 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
164 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
163 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
165 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
164 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
166 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
165 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
167 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
166 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
168 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
167 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
169 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
168 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
170 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
169 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
171 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
170 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
172 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
171 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
173 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
172 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
174 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
173 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
175 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
174 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
176 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
175 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
177 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
176 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
178 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
177 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
179 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
178 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
180 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
179 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
181 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
180 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
182 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
181 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
183 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
182 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
184 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
183 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
185 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
184 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
186 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
185 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
187 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
186 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
188 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
187 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
189 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
188 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
190 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
189 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
191 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
190 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
192 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
191 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
193 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
192 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
194 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
193 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
195 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
194 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
196 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
195 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
197 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
196 pyroutes.register('repo_changelog_elements', '/%(repo_name)s/changelog_elements', ['repo_name']);
198 pyroutes.register('repo_changelog_elements', '/%(repo_name)s/changelog_elements', ['repo_name']);
197 pyroutes.register('repo_changelog_elements_file', '/%(repo_name)s/changelog_elements/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
199 pyroutes.register('repo_changelog_elements_file', '/%(repo_name)s/changelog_elements/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
198 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
200 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
199 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
201 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
200 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
202 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
201 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
203 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
202 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
204 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
203 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
205 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
204 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
206 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
205 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
207 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
206 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
208 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
207 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
209 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
208 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
210 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
209 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
211 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
210 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
212 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
211 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
213 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
212 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
214 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
213 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
215 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
214 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
216 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
215 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
217 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
216 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
218 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
217 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
219 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
218 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
220 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
219 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
221 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
220 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
222 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
221 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
223 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
222 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
224 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
223 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
225 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
224 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
226 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
227 pyroutes.register('edit_repo_advanced_hooks', '/%(repo_name)s/settings/advanced/hooks', ['repo_name']);
225 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
228 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
226 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
229 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
227 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
230 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
228 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
231 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
229 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
232 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
230 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
233 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
231 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
234 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
232 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
235 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
233 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
236 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
234 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
237 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
235 pyroutes.register('edit_repo_remote_push', '/%(repo_name)s/settings/remote/push', ['repo_name']);
238 pyroutes.register('edit_repo_remote_push', '/%(repo_name)s/settings/remote/push', ['repo_name']);
236 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
239 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
237 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
240 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
238 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
241 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
239 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
242 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
240 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
243 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
241 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
244 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
242 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
245 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
243 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
246 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
244 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
247 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
245 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
248 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
246 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
249 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
247 pyroutes.register('repo_automation', '/%(repo_name)s/settings/automation', ['repo_name']);
250 pyroutes.register('repo_automation', '/%(repo_name)s/settings/automation', ['repo_name']);
248 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
251 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
249 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
252 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
250 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
253 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
251 pyroutes.register('edit_repo_audit_logs', '/%(repo_name)s/settings/audit_logs', ['repo_name']);
254 pyroutes.register('edit_repo_audit_logs', '/%(repo_name)s/settings/audit_logs', ['repo_name']);
252 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
255 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
253 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
256 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
254 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
257 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
255 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
258 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
256 pyroutes.register('edit_repo_group', '/%(repo_group_name)s/_edit', ['repo_group_name']);
259 pyroutes.register('edit_repo_group', '/%(repo_group_name)s/_edit', ['repo_group_name']);
257 pyroutes.register('edit_repo_group_advanced', '/%(repo_group_name)s/_settings/advanced', ['repo_group_name']);
260 pyroutes.register('edit_repo_group_advanced', '/%(repo_group_name)s/_settings/advanced', ['repo_group_name']);
258 pyroutes.register('edit_repo_group_advanced_delete', '/%(repo_group_name)s/_settings/advanced/delete', ['repo_group_name']);
261 pyroutes.register('edit_repo_group_advanced_delete', '/%(repo_group_name)s/_settings/advanced/delete', ['repo_group_name']);
259 pyroutes.register('edit_repo_group_perms', '/%(repo_group_name)s/_settings/permissions', ['repo_group_name']);
262 pyroutes.register('edit_repo_group_perms', '/%(repo_group_name)s/_settings/permissions', ['repo_group_name']);
260 pyroutes.register('edit_repo_group_perms_update', '/%(repo_group_name)s/_settings/permissions/update', ['repo_group_name']);
263 pyroutes.register('edit_repo_group_perms_update', '/%(repo_group_name)s/_settings/permissions/update', ['repo_group_name']);
261 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
264 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
262 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
265 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
263 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
266 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
264 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
267 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
265 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
268 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
266 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
269 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
267 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
270 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
268 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
271 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
269 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
272 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
270 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
273 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
271 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
274 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
272 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
275 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
273 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
276 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
274 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
277 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
275 pyroutes.register('search', '/_admin/search', []);
278 pyroutes.register('search', '/_admin/search', []);
276 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
279 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
277 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
280 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
281 pyroutes.register('user_group_profile', '/_profile_user_group/%(user_group_name)s', ['user_group_name']);
278 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
282 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
279 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
283 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
280 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
284 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
281 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
285 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
282 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
286 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
283 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
287 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
284 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
288 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
285 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
289 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
286 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
290 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
287 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
291 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
288 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
292 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
289 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
293 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
290 pyroutes.register('my_account_user_group_membership', '/_admin/my_account/user_group_membership', []);
294 pyroutes.register('my_account_user_group_membership', '/_admin/my_account/user_group_membership', []);
291 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
295 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
292 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
296 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
293 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
297 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
294 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
298 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
295 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
299 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
296 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
300 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
297 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
301 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
298 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
302 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
299 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
303 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
300 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
304 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
301 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
305 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
302 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
306 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
303 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
307 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
304 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
308 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
305 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
309 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
306 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
310 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
307 pyroutes.register('gists_show', '/_admin/gists', []);
311 pyroutes.register('gists_show', '/_admin/gists', []);
308 pyroutes.register('gists_new', '/_admin/gists/new', []);
312 pyroutes.register('gists_new', '/_admin/gists/new', []);
309 pyroutes.register('gists_create', '/_admin/gists/create', []);
313 pyroutes.register('gists_create', '/_admin/gists/create', []);
310 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
314 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
311 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
315 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
312 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
316 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
313 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
317 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
314 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
318 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
315 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
319 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
316 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
320 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
317 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
321 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
318 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
322 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
319 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
323 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
320 pyroutes.register('apiv2', '/_admin/api', []);
324 pyroutes.register('apiv2', '/_admin/api', []);
321 }
325 }
@@ -1,595 +1,850 b''
1 // # Copyright (C) 2010-2018 RhodeCode GmbH
1 // # Copyright (C) 2010-2018 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 /**
19 /**
20 * Code Mirror
20 * Code Mirror
21 */
21 */
22 // global code-mirror logger;, to enable run
22 // global code-mirror logger;, to enable run
23 // Logger.get('CodeMirror').setLevel(Logger.DEBUG)
23 // Logger.get('CodeMirror').setLevel(Logger.DEBUG)
24
24
25 cmLog = Logger.get('CodeMirror');
25 cmLog = Logger.get('CodeMirror');
26 cmLog.setLevel(Logger.OFF);
26 cmLog.setLevel(Logger.OFF);
27
27
28
28
29 //global cache for inline forms
29 //global cache for inline forms
30 var userHintsCache = {};
30 var userHintsCache = {};
31
31
32 // global timer, used to cancel async loading
32 // global timer, used to cancel async loading
33 var CodeMirrorLoadUserHintTimer;
33 var CodeMirrorLoadUserHintTimer;
34
34
35 var escapeRegExChars = function(value) {
35 var escapeRegExChars = function(value) {
36 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
36 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
37 };
37 };
38
38
39 /**
39 /**
40 * Load hints from external source returns an array of objects in a format
40 * Load hints from external source returns an array of objects in a format
41 * that hinting lib requires
41 * that hinting lib requires
42 * @returns {Array}
42 * @returns {Array}
43 */
43 */
44 var CodeMirrorLoadUserHints = function(query, triggerHints) {
44 var CodeMirrorLoadUserHints = function(query, triggerHints) {
45 cmLog.debug('Loading mentions users via AJAX');
45 cmLog.debug('Loading mentions users via AJAX');
46 var _users = [];
46 var _users = [];
47 $.ajax({
47 $.ajax({
48 type: 'GET',
48 type: 'GET',
49 data: {query: query},
49 data: {query: query},
50 url: pyroutes.url('user_autocomplete_data'),
50 url: pyroutes.url('user_autocomplete_data'),
51 headers: {'X-PARTIAL-XHR': true},
51 headers: {'X-PARTIAL-XHR': true},
52 async: true
52 async: true
53 })
53 })
54 .done(function(data) {
54 .done(function(data) {
55 var tmpl = '<img class="gravatar" src="{0}"/>{1}';
55 var tmpl = '<img class="gravatar" src="{0}"/>{1}';
56 $.each(data.suggestions, function(i) {
56 $.each(data.suggestions, function(i) {
57 var userObj = data.suggestions[i];
57 var userObj = data.suggestions[i];
58
58
59 if (userObj.username !== "default") {
59 if (userObj.username !== "default") {
60 _users.push({
60 _users.push({
61 text: userObj.username + " ",
61 text: userObj.username + " ",
62 org_text: userObj.username,
62 org_text: userObj.username,
63 displayText: userObj.value_display, // search that field
63 displayText: userObj.value_display, // search that field
64 // internal caches
64 // internal caches
65 _icon_link: userObj.icon_link,
65 _icon_link: userObj.icon_link,
66 _text: userObj.value_display,
66 _text: userObj.value_display,
67
67
68 render: function(elt, data, completion) {
68 render: function(elt, data, completion) {
69 var el = document.createElement('div');
69 var el = document.createElement('div');
70 el.className = "CodeMirror-hint-entry";
70 el.className = "CodeMirror-hint-entry";
71 el.innerHTML = tmpl.format(
71 el.innerHTML = tmpl.format(
72 completion._icon_link, completion._text);
72 completion._icon_link, completion._text);
73 elt.appendChild(el);
73 elt.appendChild(el);
74 }
74 }
75 });
75 });
76 }
76 }
77 });
77 });
78 cmLog.debug('Mention users loaded');
78 cmLog.debug('Mention users loaded');
79 // set to global cache
79 // set to global cache
80 userHintsCache[query] = _users;
80 userHintsCache[query] = _users;
81 triggerHints(userHintsCache[query]);
81 triggerHints(userHintsCache[query]);
82 })
82 })
83 .fail(function(data, textStatus, xhr) {
83 .fail(function(data, textStatus, xhr) {
84 alert("error processing request. \n" +
84 alert("error processing request. \n" +
85 "Error code {0} ({1}).".format(data.status, data.statusText));
85 "Error code {0} ({1}).".format(data.status, data.statusText));
86 });
86 });
87 };
87 };
88
88
89 /**
89 /**
90 * filters the results based on the current context
90 * filters the results based on the current context
91 * @param users
91 * @param users
92 * @param context
92 * @param context
93 * @returns {Array}
93 * @returns {Array}
94 */
94 */
95 var CodeMirrorFilterUsers = function(users, context) {
95 var CodeMirrorFilterUsers = function(users, context) {
96 var MAX_LIMIT = 10;
96 var MAX_LIMIT = 10;
97 var filtered_users = [];
97 var filtered_users = [];
98 var curWord = context.string;
98 var curWord = context.string;
99
99
100 cmLog.debug('Filtering users based on query:', curWord);
100 cmLog.debug('Filtering users based on query:', curWord);
101 $.each(users, function(i) {
101 $.each(users, function(i) {
102 var match = users[i];
102 var match = users[i];
103 var searchText = match.displayText;
103 var searchText = match.displayText;
104
104
105 if (!curWord ||
105 if (!curWord ||
106 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
106 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
107 // reset state
107 // reset state
108 match._text = match.displayText;
108 match._text = match.displayText;
109 if (curWord) {
109 if (curWord) {
110 // do highlighting
110 // do highlighting
111 var pattern = '(' + escapeRegExChars(curWord) + ')';
111 var pattern = '(' + escapeRegExChars(curWord) + ')';
112 match._text = searchText.replace(
112 match._text = searchText.replace(
113 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
113 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
114 }
114 }
115
115
116 filtered_users.push(match);
116 filtered_users.push(match);
117 }
117 }
118 // to not return to many results, use limit of filtered results
118 // to not return to many results, use limit of filtered results
119 if (filtered_users.length > MAX_LIMIT) {
119 if (filtered_users.length > MAX_LIMIT) {
120 return false;
120 return false;
121 }
121 }
122 });
122 });
123
123
124 return filtered_users;
124 return filtered_users;
125 };
125 };
126
126
127 var CodeMirrorMentionHint = function(editor, callback, options) {
127 var CodeMirrorMentionHint = function(editor, callback, options) {
128 var cur = editor.getCursor();
128 var cur = editor.getCursor();
129 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
129 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
130
130
131 // match on @ +1char
131 // match on @ +1char
132 var tokenMatch = new RegExp(
132 var tokenMatch = new RegExp(
133 '(^@| @)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]*)$').exec(curLine);
133 '(^@| @)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]*)$').exec(curLine);
134
134
135 var tokenStr = '';
135 var tokenStr = '';
136 if (tokenMatch !== null && tokenMatch.length > 0){
136 if (tokenMatch !== null && tokenMatch.length > 0){
137 tokenStr = tokenMatch[0].strip();
137 tokenStr = tokenMatch[0].strip();
138 } else {
138 } else {
139 // skip if we didn't match our token
139 // skip if we didn't match our token
140 return;
140 return;
141 }
141 }
142
142
143 var context = {
143 var context = {
144 start: (cur.ch - tokenStr.length) + 1,
144 start: (cur.ch - tokenStr.length) + 1,
145 end: cur.ch,
145 end: cur.ch,
146 string: tokenStr.slice(1),
146 string: tokenStr.slice(1),
147 type: null
147 type: null
148 };
148 };
149
149
150 // case when we put the @sign in fron of a string,
150 // case when we put the @sign in fron of a string,
151 // eg <@ we put it here>sometext then we need to prepend to text
151 // eg <@ we put it here>sometext then we need to prepend to text
152 if (context.end > cur.ch) {
152 if (context.end > cur.ch) {
153 context.start = context.start + 1; // we add to the @ sign
153 context.start = context.start + 1; // we add to the @ sign
154 context.end = cur.ch; // don't eat front part just append
154 context.end = cur.ch; // don't eat front part just append
155 context.string = context.string.slice(1, cur.ch - context.start);
155 context.string = context.string.slice(1, cur.ch - context.start);
156 }
156 }
157
157
158 cmLog.debug('Mention context', context);
158 cmLog.debug('Mention context', context);
159
159
160 var triggerHints = function(userHints){
160 var triggerHints = function(userHints){
161 return callback({
161 return callback({
162 list: CodeMirrorFilterUsers(userHints, context),
162 list: CodeMirrorFilterUsers(userHints, context),
163 from: CodeMirror.Pos(cur.line, context.start),
163 from: CodeMirror.Pos(cur.line, context.start),
164 to: CodeMirror.Pos(cur.line, context.end)
164 to: CodeMirror.Pos(cur.line, context.end)
165 });
165 });
166 };
166 };
167
167
168 var queryBasedHintsCache = undefined;
168 var queryBasedHintsCache = undefined;
169 // if we have something in the cache, try to fetch the query based cache
169 // if we have something in the cache, try to fetch the query based cache
170 if (userHintsCache !== {}){
170 if (userHintsCache !== {}){
171 queryBasedHintsCache = userHintsCache[context.string];
171 queryBasedHintsCache = userHintsCache[context.string];
172 }
172 }
173
173
174 if (queryBasedHintsCache !== undefined) {
174 if (queryBasedHintsCache !== undefined) {
175 cmLog.debug('Users loaded from cache');
175 cmLog.debug('Users loaded from cache');
176 triggerHints(queryBasedHintsCache);
176 triggerHints(queryBasedHintsCache);
177 } else {
177 } else {
178 // this takes care for async loading, and then displaying results
178 // this takes care for async loading, and then displaying results
179 // and also propagates the userHintsCache
179 // and also propagates the userHintsCache
180 window.clearTimeout(CodeMirrorLoadUserHintTimer);
180 window.clearTimeout(CodeMirrorLoadUserHintTimer);
181 CodeMirrorLoadUserHintTimer = setTimeout(function() {
181 CodeMirrorLoadUserHintTimer = setTimeout(function() {
182 CodeMirrorLoadUserHints(context.string, triggerHints);
182 CodeMirrorLoadUserHints(context.string, triggerHints);
183 }, 300);
183 }, 300);
184 }
184 }
185 };
185 };
186
186
187 var CodeMirrorCompleteAfter = function(cm, pred) {
187 var CodeMirrorCompleteAfter = function(cm, pred) {
188 var options = {
188 var options = {
189 completeSingle: false,
189 completeSingle: false,
190 async: true,
190 async: true,
191 closeOnUnfocus: true
191 closeOnUnfocus: true
192 };
192 };
193 var cur = cm.getCursor();
193 var cur = cm.getCursor();
194 setTimeout(function() {
194 setTimeout(function() {
195 if (!cm.state.completionActive) {
195 if (!cm.state.completionActive) {
196 cmLog.debug('Trigger mentions hinting');
196 cmLog.debug('Trigger mentions hinting');
197 CodeMirror.showHint(cm, CodeMirror.hint.mentions, options);
197 CodeMirror.showHint(cm, CodeMirror.hint.mentions, options);
198 }
198 }
199 }, 100);
199 }, 100);
200
200
201 // tell CodeMirror we didn't handle the key
201 // tell CodeMirror we didn't handle the key
202 // trick to trigger on a char but still complete it
202 // trick to trigger on a char but still complete it
203 return CodeMirror.Pass;
203 return CodeMirror.Pass;
204 };
204 };
205
205
206 var initCodeMirror = function(textAreadId, resetUrl, focus, options) {
206 var initCodeMirror = function(textAreadId, resetUrl, focus, options) {
207 var ta = $('#' + textAreadId).get(0);
207 var ta = $('#' + textAreadId).get(0);
208 if (focus === undefined) {
208 if (focus === undefined) {
209 focus = true;
209 focus = true;
210 }
210 }
211
211
212 // default options
212 // default options
213 var codeMirrorOptions = {
213 var codeMirrorOptions = {
214 mode: "null",
214 mode: "null",
215 lineNumbers: true,
215 lineNumbers: true,
216 indentUnit: 4,
216 indentUnit: 4,
217 autofocus: focus
217 autofocus: focus
218 };
218 };
219
219
220 if (options !== undefined) {
220 if (options !== undefined) {
221 // extend with custom options
221 // extend with custom options
222 codeMirrorOptions = $.extend(true, codeMirrorOptions, options);
222 codeMirrorOptions = $.extend(true, codeMirrorOptions, options);
223 }
223 }
224
224
225 var myCodeMirror = CodeMirror.fromTextArea(ta, codeMirrorOptions);
225 var myCodeMirror = CodeMirror.fromTextArea(ta, codeMirrorOptions);
226
226
227 $('#reset').on('click', function(e) {
227 $('#reset').on('click', function(e) {
228 window.location = resetUrl;
228 window.location = resetUrl;
229 });
229 });
230
230
231 return myCodeMirror;
231 return myCodeMirror;
232 };
232 };
233
233
234
235 var initMarkupCodeMirror = function(textAreadId, focus, options) {
236 var initialHeight = 100;
237
238 var ta = $(textAreadId).get(0);
239 if (focus === undefined) {
240 focus = true;
241 }
242
243 // default options
244 var codeMirrorOptions = {
245 lineNumbers: false,
246 indentUnit: 4,
247 viewportMargin: 30,
248 // this is a trick to trigger some logic behind codemirror placeholder
249 // it influences styling and behaviour.
250 placeholder: " ",
251 lineWrapping: true,
252 autofocus: focus
253 };
254
255 if (options !== undefined) {
256 // extend with custom options
257 codeMirrorOptions = $.extend(true, codeMirrorOptions, options);
258 }
259
260 var cm = CodeMirror.fromTextArea(ta, codeMirrorOptions);
261 cm.setSize(null, initialHeight);
262 cm.setOption("mode", DEFAULT_RENDERER);
263 CodeMirror.autoLoadMode(cm, DEFAULT_RENDERER); // load rst or markdown mode
264 cmLog.debug('Loading codemirror mode', DEFAULT_RENDERER);
265
266 // start listening on changes to make auto-expanded editor
267 cm.on("change", function(instance, changeObj) {
268 var height = initialHeight;
269 var lines = instance.lineCount();
270 if ( lines > 6 && lines < 20) {
271 height = "auto";
272 }
273 else if (lines >= 20){
274 zheight = 20*15;
275 }
276 instance.setSize(null, height);
277
278 // detect if the change was trigger by auto desc, or user input
279 var changeOrigin = changeObj.origin;
280
281 if (changeOrigin === "setValue") {
282 cmLog.debug('Change triggered by setValue');
283 }
284 else {
285 cmLog.debug('user triggered change !');
286 // set special marker to indicate user has created an input.
287 instance._userDefinedValue = true;
288 }
289
290 });
291
292 return cm;
293 };
294
295
234 var initCommentBoxCodeMirror = function(CommentForm, textAreaId, triggerActions){
296 var initCommentBoxCodeMirror = function(CommentForm, textAreaId, triggerActions){
235 var initialHeight = 100;
297 var initialHeight = 100;
236
298
237 if (typeof userHintsCache === "undefined") {
299 if (typeof userHintsCache === "undefined") {
238 userHintsCache = {};
300 userHintsCache = {};
239 cmLog.debug('Init empty cache for mentions');
301 cmLog.debug('Init empty cache for mentions');
240 }
302 }
241 if (!$(textAreaId).get(0)) {
303 if (!$(textAreaId).get(0)) {
242 cmLog.debug('Element for textarea not found', textAreaId);
304 cmLog.debug('Element for textarea not found', textAreaId);
243 return;
305 return;
244 }
306 }
245 /**
307 /**
246 * Filter action based on typed in text
308 * Filter action based on typed in text
247 * @param actions
309 * @param actions
248 * @param context
310 * @param context
249 * @returns {Array}
311 * @returns {Array}
250 */
312 */
251
313
252 var filterActions = function(actions, context){
314 var filterActions = function(actions, context){
253
315
254 var MAX_LIMIT = 10;
316 var MAX_LIMIT = 10;
255 var filtered_actions = [];
317 var filtered_actions = [];
256 var curWord = context.string;
318 var curWord = context.string;
257
319
258 cmLog.debug('Filtering actions based on query:', curWord);
320 cmLog.debug('Filtering actions based on query:', curWord);
259 $.each(actions, function(i) {
321 $.each(actions, function(i) {
260 var match = actions[i];
322 var match = actions[i];
261 var searchText = match.searchText;
323 var searchText = match.searchText;
262
324
263 if (!curWord ||
325 if (!curWord ||
264 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
326 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
265 // reset state
327 // reset state
266 match._text = match.displayText;
328 match._text = match.displayText;
267 if (curWord) {
329 if (curWord) {
268 // do highlighting
330 // do highlighting
269 var pattern = '(' + escapeRegExChars(curWord) + ')';
331 var pattern = '(' + escapeRegExChars(curWord) + ')';
270 match._text = searchText.replace(
332 match._text = searchText.replace(
271 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
333 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
272 }
334 }
273
335
274 filtered_actions.push(match);
336 filtered_actions.push(match);
275 }
337 }
276 // to not return to many results, use limit of filtered results
338 // to not return to many results, use limit of filtered results
277 if (filtered_actions.length > MAX_LIMIT) {
339 if (filtered_actions.length > MAX_LIMIT) {
278 return false;
340 return false;
279 }
341 }
280 });
342 });
281
343
282 return filtered_actions;
344 return filtered_actions;
283 };
345 };
284
346
285 var submitForm = function(cm, pred) {
347 var submitForm = function(cm, pred) {
286 $(cm.display.input.textarea.form).submit();
348 $(cm.display.input.textarea.form).submit();
287 return CodeMirror.Pass;
349 return CodeMirror.Pass;
288 };
350 };
289
351
290 var completeActions = function(actions){
352 var completeActions = function(actions){
291
353
292 var registeredActions = [];
354 var registeredActions = [];
293 var allActions = [
355 var allActions = [
294 {
356 {
295 text: "approve",
357 text: "approve",
296 searchText: "status approved",
358 searchText: "status approved",
297 displayText: _gettext('Set status to Approved'),
359 displayText: _gettext('Set status to Approved'),
298 hint: function(CodeMirror, data, completion) {
360 hint: function(CodeMirror, data, completion) {
299 CodeMirror.replaceRange("", completion.from || data.from,
361 CodeMirror.replaceRange("", completion.from || data.from,
300 completion.to || data.to, "complete");
362 completion.to || data.to, "complete");
301 $(CommentForm.statusChange).select2("val", 'approved').trigger('change');
363 $(CommentForm.statusChange).select2("val", 'approved').trigger('change');
302 },
364 },
303 render: function(elt, data, completion) {
365 render: function(elt, data, completion) {
304 var el = document.createElement('div');
366 var el = document.createElement('div');
305 el.className = "flag_status flag_status_comment_box approved pull-left";
367 el.className = "flag_status flag_status_comment_box approved pull-left";
306 elt.appendChild(el);
368 elt.appendChild(el);
307
369
308 el = document.createElement('span');
370 el = document.createElement('span');
309 el.innerHTML = completion.displayText;
371 el.innerHTML = completion.displayText;
310 elt.appendChild(el);
372 elt.appendChild(el);
311 }
373 }
312 },
374 },
313 {
375 {
314 text: "reject",
376 text: "reject",
315 searchText: "status rejected",
377 searchText: "status rejected",
316 displayText: _gettext('Set status to Rejected'),
378 displayText: _gettext('Set status to Rejected'),
317 hint: function(CodeMirror, data, completion) {
379 hint: function(CodeMirror, data, completion) {
318 CodeMirror.replaceRange("", completion.from || data.from,
380 CodeMirror.replaceRange("", completion.from || data.from,
319 completion.to || data.to, "complete");
381 completion.to || data.to, "complete");
320 $(CommentForm.statusChange).select2("val", 'rejected').trigger('change');
382 $(CommentForm.statusChange).select2("val", 'rejected').trigger('change');
321 },
383 },
322 render: function(elt, data, completion) {
384 render: function(elt, data, completion) {
323 var el = document.createElement('div');
385 var el = document.createElement('div');
324 el.className = "flag_status flag_status_comment_box rejected pull-left";
386 el.className = "flag_status flag_status_comment_box rejected pull-left";
325 elt.appendChild(el);
387 elt.appendChild(el);
326
388
327 el = document.createElement('span');
389 el = document.createElement('span');
328 el.innerHTML = completion.displayText;
390 el.innerHTML = completion.displayText;
329 elt.appendChild(el);
391 elt.appendChild(el);
330 }
392 }
331 },
393 },
332 {
394 {
333 text: "as_todo",
395 text: "as_todo",
334 searchText: "todo comment",
396 searchText: "todo comment",
335 displayText: _gettext('TODO comment'),
397 displayText: _gettext('TODO comment'),
336 hint: function(CodeMirror, data, completion) {
398 hint: function(CodeMirror, data, completion) {
337 CodeMirror.replaceRange("", completion.from || data.from,
399 CodeMirror.replaceRange("", completion.from || data.from,
338 completion.to || data.to, "complete");
400 completion.to || data.to, "complete");
339
401
340 $(CommentForm.commentType).val('todo');
402 $(CommentForm.commentType).val('todo');
341 },
403 },
342 render: function(elt, data, completion) {
404 render: function(elt, data, completion) {
343 var el = document.createElement('div');
405 var el = document.createElement('div');
344 el.className = "pull-left";
406 el.className = "pull-left";
345 elt.appendChild(el);
407 elt.appendChild(el);
346
408
347 el = document.createElement('span');
409 el = document.createElement('span');
348 el.innerHTML = completion.displayText;
410 el.innerHTML = completion.displayText;
349 elt.appendChild(el);
411 elt.appendChild(el);
350 }
412 }
351 },
413 },
352 {
414 {
353 text: "as_note",
415 text: "as_note",
354 searchText: "note comment",
416 searchText: "note comment",
355 displayText: _gettext('Note Comment'),
417 displayText: _gettext('Note Comment'),
356 hint: function(CodeMirror, data, completion) {
418 hint: function(CodeMirror, data, completion) {
357 CodeMirror.replaceRange("", completion.from || data.from,
419 CodeMirror.replaceRange("", completion.from || data.from,
358 completion.to || data.to, "complete");
420 completion.to || data.to, "complete");
359
421
360 $(CommentForm.commentType).val('note');
422 $(CommentForm.commentType).val('note');
361 },
423 },
362 render: function(elt, data, completion) {
424 render: function(elt, data, completion) {
363 var el = document.createElement('div');
425 var el = document.createElement('div');
364 el.className = "pull-left";
426 el.className = "pull-left";
365 elt.appendChild(el);
427 elt.appendChild(el);
366
428
367 el = document.createElement('span');
429 el = document.createElement('span');
368 el.innerHTML = completion.displayText;
430 el.innerHTML = completion.displayText;
369 elt.appendChild(el);
431 elt.appendChild(el);
370 }
432 }
371 }
433 }
372 ];
434 ];
373
435
374 $.each(allActions, function(index, value){
436 $.each(allActions, function(index, value){
375 var actionData = allActions[index];
437 var actionData = allActions[index];
376 if (actions.indexOf(actionData['text']) != -1) {
438 if (actions.indexOf(actionData['text']) != -1) {
377 registeredActions.push(actionData);
439 registeredActions.push(actionData);
378 }
440 }
379 });
441 });
380
442
381 return function(cm, pred) {
443 return function(cm, pred) {
382 var cur = cm.getCursor();
444 var cur = cm.getCursor();
383 var options = {
445 var options = {
384 closeOnUnfocus: true,
446 closeOnUnfocus: true,
385 registeredActions: registeredActions
447 registeredActions: registeredActions
386 };
448 };
387 setTimeout(function() {
449 setTimeout(function() {
388 if (!cm.state.completionActive) {
450 if (!cm.state.completionActive) {
389 cmLog.debug('Trigger actions hinting');
451 cmLog.debug('Trigger actions hinting');
390 CodeMirror.showHint(cm, CodeMirror.hint.actions, options);
452 CodeMirror.showHint(cm, CodeMirror.hint.actions, options);
391 }
453 }
392 }, 100);
454 }, 100);
393
455
394 // tell CodeMirror we didn't handle the key
456 // tell CodeMirror we didn't handle the key
395 // trick to trigger on a char but still complete it
457 // trick to trigger on a char but still complete it
396 return CodeMirror.Pass;
458 return CodeMirror.Pass;
397 }
459 }
398 };
460 };
399
461
400 var extraKeys = {
462 var extraKeys = {
401 "'@'": CodeMirrorCompleteAfter,
463 "'@'": CodeMirrorCompleteAfter,
402 Tab: function(cm) {
464 Tab: function(cm) {
403 // space indent instead of TABS
465 // space indent instead of TABS
404 var spaces = new Array(cm.getOption("indentUnit") + 1).join(" ");
466 var spaces = new Array(cm.getOption("indentUnit") + 1).join(" ");
405 cm.replaceSelection(spaces);
467 cm.replaceSelection(spaces);
406 }
468 }
407 };
469 };
408 // submit form on Meta-Enter
470 // submit form on Meta-Enter
409 if (OSType === "mac") {
471 if (OSType === "mac") {
410 extraKeys["Cmd-Enter"] = submitForm;
472 extraKeys["Cmd-Enter"] = submitForm;
411 }
473 }
412 else {
474 else {
413 extraKeys["Ctrl-Enter"] = submitForm;
475 extraKeys["Ctrl-Enter"] = submitForm;
414 }
476 }
415
477
416 if (triggerActions) {
478 if (triggerActions) {
417 // register triggerActions for this instance
479 // register triggerActions for this instance
418 extraKeys["'/'"] = completeActions(triggerActions);
480 extraKeys["'/'"] = completeActions(triggerActions);
419 }
481 }
420
482
421 var cm = CodeMirror.fromTextArea($(textAreaId).get(0), {
483 var cm = CodeMirror.fromTextArea($(textAreaId).get(0), {
422 lineNumbers: false,
484 lineNumbers: false,
423 indentUnit: 4,
485 indentUnit: 4,
424 viewportMargin: 30,
486 viewportMargin: 30,
425 // this is a trick to trigger some logic behind codemirror placeholder
487 // this is a trick to trigger some logic behind codemirror placeholder
426 // it influences styling and behaviour.
488 // it influences styling and behaviour.
427 placeholder: " ",
489 placeholder: " ",
428 extraKeys: extraKeys,
490 extraKeys: extraKeys,
429 lineWrapping: true
491 lineWrapping: true
430 });
492 });
431
493
432 cm.setSize(null, initialHeight);
494 cm.setSize(null, initialHeight);
433 cm.setOption("mode", DEFAULT_RENDERER);
495 cm.setOption("mode", DEFAULT_RENDERER);
434 CodeMirror.autoLoadMode(cm, DEFAULT_RENDERER); // load rst or markdown mode
496 CodeMirror.autoLoadMode(cm, DEFAULT_RENDERER); // load rst or markdown mode
435 cmLog.debug('Loading codemirror mode', DEFAULT_RENDERER);
497 cmLog.debug('Loading codemirror mode', DEFAULT_RENDERER);
436 // start listening on changes to make auto-expanded editor
498 // start listening on changes to make auto-expanded editor
437 cm.on("change", function(self) {
499 cm.on("change", function(self) {
438 var height = initialHeight;
500 var height = initialHeight;
439 var lines = self.lineCount();
501 var lines = self.lineCount();
440 if ( lines > 6 && lines < 20) {
502 if ( lines > 6 && lines < 20) {
441 height = "auto";
503 height = "auto";
442 }
504 }
443 else if (lines >= 20){
505 else if (lines >= 20){
444 zheight = 20*15;
506 zheight = 20*15;
445 }
507 }
446 self.setSize(null, height);
508 self.setSize(null, height);
447 });
509 });
448
510
449 var actionHint = function(editor, options) {
511 var actionHint = function(editor, options) {
450
512
451 var cur = editor.getCursor();
513 var cur = editor.getCursor();
452 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
514 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
453
515
454 // match only on /+1 character minimum
516 // match only on /+1 character minimum
455 var tokenMatch = new RegExp('(^/\|/\)([a-zA-Z]*)$').exec(curLine);
517 var tokenMatch = new RegExp('(^/\|/\)([a-zA-Z]*)$').exec(curLine);
456
518
457 var tokenStr = '';
519 var tokenStr = '';
458 if (tokenMatch !== null && tokenMatch.length > 0){
520 if (tokenMatch !== null && tokenMatch.length > 0){
459 tokenStr = tokenMatch[2].strip();
521 tokenStr = tokenMatch[2].strip();
460 }
522 }
461
523
462 var context = {
524 var context = {
463 start: (cur.ch - tokenStr.length) - 1,
525 start: (cur.ch - tokenStr.length) - 1,
464 end: cur.ch,
526 end: cur.ch,
465 string: tokenStr,
527 string: tokenStr,
466 type: null
528 type: null
467 };
529 };
468
530
469 return {
531 return {
470 list: filterActions(options.registeredActions, context),
532 list: filterActions(options.registeredActions, context),
471 from: CodeMirror.Pos(cur.line, context.start),
533 from: CodeMirror.Pos(cur.line, context.start),
472 to: CodeMirror.Pos(cur.line, context.end)
534 to: CodeMirror.Pos(cur.line, context.end)
473 };
535 };
474
536
475 };
537 };
476 CodeMirror.registerHelper("hint", "mentions", CodeMirrorMentionHint);
538 CodeMirror.registerHelper("hint", "mentions", CodeMirrorMentionHint);
477 CodeMirror.registerHelper("hint", "actions", actionHint);
539 CodeMirror.registerHelper("hint", "actions", actionHint);
478 return cm;
540 return cm;
479 };
541 };
480
542
481 var setCodeMirrorMode = function(codeMirrorInstance, mode) {
543 var setCodeMirrorMode = function(codeMirrorInstance, mode) {
482 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
544 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
483 codeMirrorInstance.setOption("mode", mode);
545 codeMirrorInstance.setOption("mode", mode);
484 };
546 };
485
547
486 var setCodeMirrorLineWrap = function(codeMirrorInstance, line_wrap) {
548 var setCodeMirrorLineWrap = function(codeMirrorInstance, line_wrap) {
487 codeMirrorInstance.setOption("lineWrapping", line_wrap);
549 codeMirrorInstance.setOption("lineWrapping", line_wrap);
488 };
550 };
489
551
490 var setCodeMirrorModeFromSelect = function(
552 var setCodeMirrorModeFromSelect = function(
491 targetSelect, targetFileInput, codeMirrorInstance, callback){
553 targetSelect, targetFileInput, codeMirrorInstance, callback){
492
554
493 $(targetSelect).on('change', function(e) {
555 $(targetSelect).on('change', function(e) {
494 cmLog.debug('codemirror select2 mode change event !');
556 cmLog.debug('codemirror select2 mode change event !');
495 var selected = e.currentTarget;
557 var selected = e.currentTarget;
496 var node = selected.options[selected.selectedIndex];
558 var node = selected.options[selected.selectedIndex];
497 var mimetype = node.value;
559 var mimetype = node.value;
498 cmLog.debug('picked mimetype', mimetype);
560 cmLog.debug('picked mimetype', mimetype);
499 var new_mode = $(node).attr('mode');
561 var new_mode = $(node).attr('mode');
500 setCodeMirrorMode(codeMirrorInstance, new_mode);
562 setCodeMirrorMode(codeMirrorInstance, new_mode);
501 cmLog.debug('set new mode', new_mode);
563 cmLog.debug('set new mode', new_mode);
502
564
503 //propose filename from picked mode
565 //propose filename from picked mode
504 cmLog.debug('setting mimetype', mimetype);
566 cmLog.debug('setting mimetype', mimetype);
505 var proposed_ext = getExtFromMimeType(mimetype);
567 var proposed_ext = getExtFromMimeType(mimetype);
506 cmLog.debug('file input', $(targetFileInput).val());
568 cmLog.debug('file input', $(targetFileInput).val());
507 var file_data = getFilenameAndExt($(targetFileInput).val());
569 var file_data = getFilenameAndExt($(targetFileInput).val());
508 var filename = file_data.filename || 'filename1';
570 var filename = file_data.filename || 'filename1';
509 $(targetFileInput).val(filename + proposed_ext);
571 $(targetFileInput).val(filename + proposed_ext);
510 cmLog.debug('proposed file', filename + proposed_ext);
572 cmLog.debug('proposed file', filename + proposed_ext);
511
573
512
574
513 if (typeof(callback) === 'function') {
575 if (typeof(callback) === 'function') {
514 try {
576 try {
515 cmLog.debug('running callback', callback);
577 cmLog.debug('running callback', callback);
516 callback(filename, mimetype, new_mode);
578 callback(filename, mimetype, new_mode);
517 } catch (err) {
579 } catch (err) {
518 console.log('failed to run callback', callback, err);
580 console.log('failed to run callback', callback, err);
519 }
581 }
520 }
582 }
521 cmLog.debug('finish iteration...');
583 cmLog.debug('finish iteration...');
522 });
584 });
523 };
585 };
524
586
525 var setCodeMirrorModeFromInput = function(
587 var setCodeMirrorModeFromInput = function(
526 targetSelect, targetFileInput, codeMirrorInstance, callback) {
588 targetSelect, targetFileInput, codeMirrorInstance, callback) {
527
589
528 // on type the new filename set mode
590 // on type the new filename set mode
529 $(targetFileInput).on('keyup', function(e) {
591 $(targetFileInput).on('keyup', function(e) {
530 var file_data = getFilenameAndExt(this.value);
592 var file_data = getFilenameAndExt(this.value);
531 if (file_data.ext === null) {
593 if (file_data.ext === null) {
532 return;
594 return;
533 }
595 }
534
596
535 var mimetypes = getMimeTypeFromExt(file_data.ext, true);
597 var mimetypes = getMimeTypeFromExt(file_data.ext, true);
536 cmLog.debug('mimetype from file', file_data, mimetypes);
598 cmLog.debug('mimetype from file', file_data, mimetypes);
537 var detected_mode;
599 var detected_mode;
538 var detected_option;
600 var detected_option;
539 for (var i in mimetypes) {
601 for (var i in mimetypes) {
540 var mt = mimetypes[i];
602 var mt = mimetypes[i];
541 if (!detected_mode) {
603 if (!detected_mode) {
542 detected_mode = detectCodeMirrorMode(this.value, mt);
604 detected_mode = detectCodeMirrorMode(this.value, mt);
543 }
605 }
544
606
545 if (!detected_option) {
607 if (!detected_option) {
546 cmLog.debug('#mimetype option[value="{0}"]'.format(mt));
608 cmLog.debug('#mimetype option[value="{0}"]'.format(mt));
547 if ($(targetSelect).find('option[value="{0}"]'.format(mt)).length) {
609 if ($(targetSelect).find('option[value="{0}"]'.format(mt)).length) {
548 detected_option = mt;
610 detected_option = mt;
549 }
611 }
550 }
612 }
551 }
613 }
552
614
553 cmLog.debug('detected mode', detected_mode);
615 cmLog.debug('detected mode', detected_mode);
554 cmLog.debug('detected option', detected_option);
616 cmLog.debug('detected option', detected_option);
555 if (detected_mode && detected_option){
617 if (detected_mode && detected_option){
556
618
557 $(targetSelect).select2("val", detected_option);
619 $(targetSelect).select2("val", detected_option);
558 setCodeMirrorMode(codeMirrorInstance, detected_mode);
620 setCodeMirrorMode(codeMirrorInstance, detected_mode);
559
621
560 if(typeof(callback) === 'function'){
622 if(typeof(callback) === 'function'){
561 try{
623 try{
562 cmLog.debug('running callback', callback);
624 cmLog.debug('running callback', callback);
563 var filename = file_data.filename + "." + file_data.ext;
625 var filename = file_data.filename + "." + file_data.ext;
564 callback(filename, detected_option, detected_mode);
626 callback(filename, detected_option, detected_mode);
565 }catch (err){
627 }catch (err){
566 console.log('failed to run callback', callback, err);
628 console.log('failed to run callback', callback, err);
567 }
629 }
568 }
630 }
569 }
631 }
570
632
571 });
633 });
572 };
634 };
573
635
574 var fillCodeMirrorOptions = function(targetSelect) {
636 var fillCodeMirrorOptions = function(targetSelect) {
575 //inject new modes, based on codeMirrors modeInfo object
637 //inject new modes, based on codeMirrors modeInfo object
576 var modes_select = $(targetSelect);
638 var modes_select = $(targetSelect);
577 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
639 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
578 var m = CodeMirror.modeInfo[i];
640 var m = CodeMirror.modeInfo[i];
579 var opt = new Option(m.name, m.mime);
641 var opt = new Option(m.name, m.mime);
580 $(opt).attr('mode', m.mode);
642 $(opt).attr('mode', m.mode);
581 modes_select.append(opt);
643 modes_select.append(opt);
582 }
644 }
583 };
645 };
584
646
585 var CodeMirrorPreviewEnable = function(edit_mode) {
647 var CodeMirrorPreviewEnable = function(edit_mode) {
586 // in case it a preview enabled mode enable the button
648 // in case it a preview enabled mode enable the button
587 if (['markdown', 'rst', 'gfm'].indexOf(edit_mode) !== -1) {
649 if (['markdown', 'rst', 'gfm'].indexOf(edit_mode) !== -1) {
588 $('#render_preview').removeClass('hidden');
650 $('#render_preview').removeClass('hidden');
589 }
651 }
590 else {
652 else {
591 if (!$('#render_preview').hasClass('hidden')) {
653 if (!$('#render_preview').hasClass('hidden')) {
592 $('#render_preview').addClass('hidden');
654 $('#render_preview').addClass('hidden');
593 }
655 }
594 }
656 }
595 };
657 };
658
659
660 /* markup form */
661 (function(mod) {
662
663 if (typeof exports == "object" && typeof module == "object") {
664 // CommonJS
665 module.exports = mod();
666 }
667 else {
668 // Plain browser env
669 (this || window).MarkupForm = mod();
670 }
671
672 })(function() {
673 "use strict";
674
675 function MarkupForm(textareaId) {
676 if (!(this instanceof MarkupForm)) {
677 return new MarkupForm(textareaId);
678 }
679
680 // bind the element instance to our Form
681 $('#' + textareaId).get(0).MarkupForm = this;
682
683 this.withSelectorId = function(selector) {
684 var selectorId = textareaId;
685 return selector + '_' + selectorId;
686 };
687
688 this.previewButton = this.withSelectorId('#preview-btn');
689 this.previewContainer = this.withSelectorId('#preview-container');
690
691 this.previewBoxSelector = this.withSelectorId('#preview-box');
692
693 this.editButton = this.withSelectorId('#edit-btn');
694 this.editContainer = this.withSelectorId('#edit-container');
695
696 this.cmBox = textareaId;
697 this.cm = initMarkupCodeMirror('#' + textareaId);
698
699 this.previewUrl = pyroutes.url('markup_preview');
700
701 // FUNCTIONS and helpers
702 var self = this;
703
704 this.getCmInstance = function(){
705 return this.cm
706 };
707
708 this.setPlaceholder = function(placeholder) {
709 var cm = this.getCmInstance();
710 if (cm){
711 cm.setOption('placeholder', placeholder);
712 }
713 };
714
715 this.initStatusChangeSelector = function(){
716 var formatChangeStatus = function(state, escapeMarkup) {
717 var originalOption = state.element;
718 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
719 '<span>' + escapeMarkup(state.text) + '</span>';
720 };
721 var formatResult = function(result, container, query, escapeMarkup) {
722 return formatChangeStatus(result, escapeMarkup);
723 };
724
725 var formatSelection = function(data, container, escapeMarkup) {
726 return formatChangeStatus(data, escapeMarkup);
727 };
728
729 $(this.submitForm).find(this.statusChange).select2({
730 placeholder: _gettext('Status Review'),
731 formatResult: formatResult,
732 formatSelection: formatSelection,
733 containerCssClass: "drop-menu status_box_menu",
734 dropdownCssClass: "drop-menu-dropdown",
735 dropdownAutoWidth: true,
736 minimumResultsForSearch: -1
737 });
738 $(this.submitForm).find(this.statusChange).on('change', function() {
739 var status = self.getCommentStatus();
740
741 if (status && !self.isInline()) {
742 $(self.submitButton).prop('disabled', false);
743 }
744
745 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
746 self.setPlaceholder(placeholderText)
747 })
748 };
749
750 // reset the text area into it's original state
751 this.resetMarkupFormState = function(content) {
752 content = content || '';
753
754 $(this.editContainer).show();
755 $(this.editButton).parent().addClass('active');
756
757 $(this.previewContainer).hide();
758 $(this.previewButton).parent().removeClass('active');
759
760 this.setActionButtonsDisabled(true);
761 self.cm.setValue(content);
762 self.cm.setOption("readOnly", false);
763 };
764
765 this.previewSuccessCallback = function(o) {
766 $(self.previewBoxSelector).html(o);
767 $(self.previewBoxSelector).removeClass('unloaded');
768
769 // swap buttons, making preview active
770 $(self.previewButton).parent().addClass('active');
771 $(self.editButton).parent().removeClass('active');
772
773 // unlock buttons
774 self.setActionButtonsDisabled(false);
775 };
776
777 this.setActionButtonsDisabled = function(state) {
778 $(this.editButton).prop('disabled', state);
779 $(this.previewButton).prop('disabled', state);
780 };
781
782 // lock preview/edit/submit buttons on load, but exclude cancel button
783 var excludeCancelBtn = true;
784 this.setActionButtonsDisabled(true);
785
786 // anonymous users don't have access to initialized CM instance
787 if (this.cm !== undefined){
788 this.cm.on('change', function(cMirror) {
789 if (cMirror.getValue() === "") {
790 self.setActionButtonsDisabled(true)
791 } else {
792 self.setActionButtonsDisabled(false)
793 }
794 });
795 }
796
797 $(this.editButton).on('click', function(e) {
798 e.preventDefault();
799
800 $(self.previewButton).parent().removeClass('active');
801 $(self.previewContainer).hide();
802
803 $(self.editButton).parent().addClass('active');
804 $(self.editContainer).show();
805
806 });
807
808 $(this.previewButton).on('click', function(e) {
809 e.preventDefault();
810 var text = self.cm.getValue();
811
812 if (text === "") {
813 return;
814 }
815
816 var postData = {
817 'text': text,
818 'renderer': templateContext.visual.default_renderer,
819 'csrf_token': CSRF_TOKEN
820 };
821
822 // lock ALL buttons on preview
823 self.setActionButtonsDisabled(true);
824
825 $(self.previewBoxSelector).addClass('unloaded');
826 $(self.previewBoxSelector).html(_gettext('Loading ...'));
827
828 $(self.editContainer).hide();
829 $(self.previewContainer).show();
830
831 // by default we reset state of comment preserving the text
832 var previewFailCallback = function(data){
833 alert(
834 "Error while submitting preview.\n" +
835 "Error code {0} ({1}).".format(data.status, data.statusText)
836 );
837 self.resetMarkupFormState(text)
838 };
839 _submitAjaxPOST(
840 self.previewUrl, postData, self.previewSuccessCallback,
841 previewFailCallback);
842
843 $(self.previewButton).parent().addClass('active');
844 $(self.editButton).parent().removeClass('active');
845 });
846
847 }
848
849 return MarkupForm;
850 });
@@ -1,811 +1,829 b''
1 // # Copyright (C) 2010-2018 RhodeCode GmbH
1 // # Copyright (C) 2010-2018 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 var firefoxAnchorFix = function() {
19 var firefoxAnchorFix = function() {
20 // hack to make anchor links behave properly on firefox, in our inline
20 // hack to make anchor links behave properly on firefox, in our inline
21 // comments generation when comments are injected firefox is misbehaving
21 // comments generation when comments are injected firefox is misbehaving
22 // when jumping to anchor links
22 // when jumping to anchor links
23 if (location.href.indexOf('#') > -1) {
23 if (location.href.indexOf('#') > -1) {
24 location.href += '';
24 location.href += '';
25 }
25 }
26 };
26 };
27
27
28 var linkifyComments = function(comments) {
28 var linkifyComments = function(comments) {
29 var firstCommentId = null;
29 var firstCommentId = null;
30 if (comments) {
30 if (comments) {
31 firstCommentId = $(comments[0]).data('comment-id');
31 firstCommentId = $(comments[0]).data('comment-id');
32 }
32 }
33
33
34 if (firstCommentId){
34 if (firstCommentId){
35 $('#inline-comments-counter').attr('href', '#comment-' + firstCommentId);
35 $('#inline-comments-counter').attr('href', '#comment-' + firstCommentId);
36 }
36 }
37 };
37 };
38
38
39 var bindToggleButtons = function() {
39 var bindToggleButtons = function() {
40 $('.comment-toggle').on('click', function() {
40 $('.comment-toggle').on('click', function() {
41 $(this).parent().nextUntil('tr.line').toggle('inline-comments');
41 $(this).parent().nextUntil('tr.line').toggle('inline-comments');
42 });
42 });
43 };
43 };
44
44
45
46
47 var _submitAjaxPOST = function(url, postData, successHandler, failHandler) {
48 failHandler = failHandler || function() {};
49 postData = toQueryString(postData);
50 var request = $.ajax({
51 url: url,
52 type: 'POST',
53 data: postData,
54 headers: {'X-PARTIAL-XHR': true}
55 })
56 .done(function (data) {
57 successHandler(data);
58 })
59 .fail(function (data, textStatus, errorThrown) {
60 failHandler(data, textStatus, errorThrown)
61 });
62 return request;
63 };
64
65
66
67
45 /* Comment form for main and inline comments */
68 /* Comment form for main and inline comments */
46 (function(mod) {
69 (function(mod) {
47
70
48 if (typeof exports == "object" && typeof module == "object") {
71 if (typeof exports == "object" && typeof module == "object") {
49 // CommonJS
72 // CommonJS
50 module.exports = mod();
73 module.exports = mod();
51 }
74 }
52 else {
75 else {
53 // Plain browser env
76 // Plain browser env
54 (this || window).CommentForm = mod();
77 (this || window).CommentForm = mod();
55 }
78 }
56
79
57 })(function() {
80 })(function() {
58 "use strict";
81 "use strict";
59
82
60 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId) {
83 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId) {
61 if (!(this instanceof CommentForm)) {
84 if (!(this instanceof CommentForm)) {
62 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId);
85 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId);
63 }
86 }
64
87
65 // bind the element instance to our Form
88 // bind the element instance to our Form
66 $(formElement).get(0).CommentForm = this;
89 $(formElement).get(0).CommentForm = this;
67
90
68 this.withLineNo = function(selector) {
91 this.withLineNo = function(selector) {
69 var lineNo = this.lineNo;
92 var lineNo = this.lineNo;
70 if (lineNo === undefined) {
93 if (lineNo === undefined) {
71 return selector
94 return selector
72 } else {
95 } else {
73 return selector + '_' + lineNo;
96 return selector + '_' + lineNo;
74 }
97 }
75 };
98 };
76
99
77 this.commitId = commitId;
100 this.commitId = commitId;
78 this.pullRequestId = pullRequestId;
101 this.pullRequestId = pullRequestId;
79 this.lineNo = lineNo;
102 this.lineNo = lineNo;
80 this.initAutocompleteActions = initAutocompleteActions;
103 this.initAutocompleteActions = initAutocompleteActions;
81
104
82 this.previewButton = this.withLineNo('#preview-btn');
105 this.previewButton = this.withLineNo('#preview-btn');
83 this.previewContainer = this.withLineNo('#preview-container');
106 this.previewContainer = this.withLineNo('#preview-container');
84
107
85 this.previewBoxSelector = this.withLineNo('#preview-box');
108 this.previewBoxSelector = this.withLineNo('#preview-box');
86
109
87 this.editButton = this.withLineNo('#edit-btn');
110 this.editButton = this.withLineNo('#edit-btn');
88 this.editContainer = this.withLineNo('#edit-container');
111 this.editContainer = this.withLineNo('#edit-container');
89 this.cancelButton = this.withLineNo('#cancel-btn');
112 this.cancelButton = this.withLineNo('#cancel-btn');
90 this.commentType = this.withLineNo('#comment_type');
113 this.commentType = this.withLineNo('#comment_type');
91
114
92 this.resolvesId = null;
115 this.resolvesId = null;
93 this.resolvesActionId = null;
116 this.resolvesActionId = null;
94
117
95 this.closesPr = '#close_pull_request';
118 this.closesPr = '#close_pull_request';
96
119
97 this.cmBox = this.withLineNo('#text');
120 this.cmBox = this.withLineNo('#text');
98 this.cm = initCommentBoxCodeMirror(this, this.cmBox, this.initAutocompleteActions);
121 this.cm = initCommentBoxCodeMirror(this, this.cmBox, this.initAutocompleteActions);
99
122
100 this.statusChange = this.withLineNo('#change_status');
123 this.statusChange = this.withLineNo('#change_status');
101
124
102 this.submitForm = formElement;
125 this.submitForm = formElement;
103 this.submitButton = $(this.submitForm).find('input[type="submit"]');
126 this.submitButton = $(this.submitForm).find('input[type="submit"]');
104 this.submitButtonText = this.submitButton.val();
127 this.submitButtonText = this.submitButton.val();
105
128
106 this.previewUrl = pyroutes.url('repo_commit_comment_preview',
129 this.previewUrl = pyroutes.url('repo_commit_comment_preview',
107 {'repo_name': templateContext.repo_name,
130 {'repo_name': templateContext.repo_name,
108 'commit_id': templateContext.commit_data.commit_id});
131 'commit_id': templateContext.commit_data.commit_id});
109
132
110 if (resolvesCommentId){
133 if (resolvesCommentId){
111 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
134 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
112 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
135 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
113 $(this.commentType).prop('disabled', true);
136 $(this.commentType).prop('disabled', true);
114 $(this.commentType).addClass('disabled');
137 $(this.commentType).addClass('disabled');
115
138
116 // disable select
139 // disable select
117 setTimeout(function() {
140 setTimeout(function() {
118 $(self.statusChange).select2('readonly', true);
141 $(self.statusChange).select2('readonly', true);
119 }, 10);
142 }, 10);
120
143
121 var resolvedInfo = (
144 var resolvedInfo = (
122 '<li class="resolve-action">' +
145 '<li class="resolve-action">' +
123 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
146 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
124 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
147 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
125 '</li>'
148 '</li>'
126 ).format(resolvesCommentId, _gettext('resolve comment'));
149 ).format(resolvesCommentId, _gettext('resolve comment'));
127 $(resolvedInfo).insertAfter($(this.commentType).parent());
150 $(resolvedInfo).insertAfter($(this.commentType).parent());
128 }
151 }
129
152
130 // based on commitId, or pullRequestId decide where do we submit
153 // based on commitId, or pullRequestId decide where do we submit
131 // out data
154 // out data
132 if (this.commitId){
155 if (this.commitId){
133 this.submitUrl = pyroutes.url('repo_commit_comment_create',
156 this.submitUrl = pyroutes.url('repo_commit_comment_create',
134 {'repo_name': templateContext.repo_name,
157 {'repo_name': templateContext.repo_name,
135 'commit_id': this.commitId});
158 'commit_id': this.commitId});
136 this.selfUrl = pyroutes.url('repo_commit',
159 this.selfUrl = pyroutes.url('repo_commit',
137 {'repo_name': templateContext.repo_name,
160 {'repo_name': templateContext.repo_name,
138 'commit_id': this.commitId});
161 'commit_id': this.commitId});
139
162
140 } else if (this.pullRequestId) {
163 } else if (this.pullRequestId) {
141 this.submitUrl = pyroutes.url('pullrequest_comment_create',
164 this.submitUrl = pyroutes.url('pullrequest_comment_create',
142 {'repo_name': templateContext.repo_name,
165 {'repo_name': templateContext.repo_name,
143 'pull_request_id': this.pullRequestId});
166 'pull_request_id': this.pullRequestId});
144 this.selfUrl = pyroutes.url('pullrequest_show',
167 this.selfUrl = pyroutes.url('pullrequest_show',
145 {'repo_name': templateContext.repo_name,
168 {'repo_name': templateContext.repo_name,
146 'pull_request_id': this.pullRequestId});
169 'pull_request_id': this.pullRequestId});
147
170
148 } else {
171 } else {
149 throw new Error(
172 throw new Error(
150 'CommentForm requires pullRequestId, or commitId to be specified.')
173 'CommentForm requires pullRequestId, or commitId to be specified.')
151 }
174 }
152
175
153 // FUNCTIONS and helpers
176 // FUNCTIONS and helpers
154 var self = this;
177 var self = this;
155
178
156 this.isInline = function(){
179 this.isInline = function(){
157 return this.lineNo && this.lineNo != 'general';
180 return this.lineNo && this.lineNo != 'general';
158 };
181 };
159
182
160 this.getCmInstance = function(){
183 this.getCmInstance = function(){
161 return this.cm
184 return this.cm
162 };
185 };
163
186
164 this.setPlaceholder = function(placeholder) {
187 this.setPlaceholder = function(placeholder) {
165 var cm = this.getCmInstance();
188 var cm = this.getCmInstance();
166 if (cm){
189 if (cm){
167 cm.setOption('placeholder', placeholder);
190 cm.setOption('placeholder', placeholder);
168 }
191 }
169 };
192 };
170
193
171 this.getCommentStatus = function() {
194 this.getCommentStatus = function() {
172 return $(this.submitForm).find(this.statusChange).val();
195 return $(this.submitForm).find(this.statusChange).val();
173 };
196 };
174 this.getCommentType = function() {
197 this.getCommentType = function() {
175 return $(this.submitForm).find(this.commentType).val();
198 return $(this.submitForm).find(this.commentType).val();
176 };
199 };
177
200
178 this.getResolvesId = function() {
201 this.getResolvesId = function() {
179 return $(this.submitForm).find(this.resolvesId).val() || null;
202 return $(this.submitForm).find(this.resolvesId).val() || null;
180 };
203 };
181
204
182 this.getClosePr = function() {
205 this.getClosePr = function() {
183 return $(this.submitForm).find(this.closesPr).val() || null;
206 return $(this.submitForm).find(this.closesPr).val() || null;
184 };
207 };
185
208
186 this.markCommentResolved = function(resolvedCommentId){
209 this.markCommentResolved = function(resolvedCommentId){
187 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show();
210 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show();
188 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide();
211 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide();
189 };
212 };
190
213
191 this.isAllowedToSubmit = function() {
214 this.isAllowedToSubmit = function() {
192 return !$(this.submitButton).prop('disabled');
215 return !$(this.submitButton).prop('disabled');
193 };
216 };
194
217
195 this.initStatusChangeSelector = function(){
218 this.initStatusChangeSelector = function(){
196 var formatChangeStatus = function(state, escapeMarkup) {
219 var formatChangeStatus = function(state, escapeMarkup) {
197 var originalOption = state.element;
220 var originalOption = state.element;
198 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
221 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
199 '<span>' + escapeMarkup(state.text) + '</span>';
222 '<span>' + escapeMarkup(state.text) + '</span>';
200 };
223 };
201 var formatResult = function(result, container, query, escapeMarkup) {
224 var formatResult = function(result, container, query, escapeMarkup) {
202 return formatChangeStatus(result, escapeMarkup);
225 return formatChangeStatus(result, escapeMarkup);
203 };
226 };
204
227
205 var formatSelection = function(data, container, escapeMarkup) {
228 var formatSelection = function(data, container, escapeMarkup) {
206 return formatChangeStatus(data, escapeMarkup);
229 return formatChangeStatus(data, escapeMarkup);
207 };
230 };
208
231
209 $(this.submitForm).find(this.statusChange).select2({
232 $(this.submitForm).find(this.statusChange).select2({
210 placeholder: _gettext('Status Review'),
233 placeholder: _gettext('Status Review'),
211 formatResult: formatResult,
234 formatResult: formatResult,
212 formatSelection: formatSelection,
235 formatSelection: formatSelection,
213 containerCssClass: "drop-menu status_box_menu",
236 containerCssClass: "drop-menu status_box_menu",
214 dropdownCssClass: "drop-menu-dropdown",
237 dropdownCssClass: "drop-menu-dropdown",
215 dropdownAutoWidth: true,
238 dropdownAutoWidth: true,
216 minimumResultsForSearch: -1
239 minimumResultsForSearch: -1
217 });
240 });
218 $(this.submitForm).find(this.statusChange).on('change', function() {
241 $(this.submitForm).find(this.statusChange).on('change', function() {
219 var status = self.getCommentStatus();
242 var status = self.getCommentStatus();
220
243
221 if (status && !self.isInline()) {
244 if (status && !self.isInline()) {
222 $(self.submitButton).prop('disabled', false);
245 $(self.submitButton).prop('disabled', false);
223 }
246 }
224
247
225 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
248 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
226 self.setPlaceholder(placeholderText)
249 self.setPlaceholder(placeholderText)
227 })
250 })
228 };
251 };
229
252
230 // reset the comment form into it's original state
253 // reset the comment form into it's original state
231 this.resetCommentFormState = function(content) {
254 this.resetCommentFormState = function(content) {
232 content = content || '';
255 content = content || '';
233
256
234 $(this.editContainer).show();
257 $(this.editContainer).show();
235 $(this.editButton).parent().addClass('active');
258 $(this.editButton).parent().addClass('active');
236
259
237 $(this.previewContainer).hide();
260 $(this.previewContainer).hide();
238 $(this.previewButton).parent().removeClass('active');
261 $(this.previewButton).parent().removeClass('active');
239
262
240 this.setActionButtonsDisabled(true);
263 this.setActionButtonsDisabled(true);
241 self.cm.setValue(content);
264 self.cm.setValue(content);
242 self.cm.setOption("readOnly", false);
265 self.cm.setOption("readOnly", false);
243
266
244 if (this.resolvesId) {
267 if (this.resolvesId) {
245 // destroy the resolve action
268 // destroy the resolve action
246 $(this.resolvesId).parent().remove();
269 $(this.resolvesId).parent().remove();
247 }
270 }
248 // reset closingPR flag
271 // reset closingPR flag
249 $('.close-pr-input').remove();
272 $('.close-pr-input').remove();
250
273
251 $(this.statusChange).select2('readonly', false);
274 $(this.statusChange).select2('readonly', false);
252 };
275 };
253
276
254 this.globalSubmitSuccessCallback = function(){
277 this.globalSubmitSuccessCallback = function(){
255 // default behaviour is to call GLOBAL hook, if it's registered.
278 // default behaviour is to call GLOBAL hook, if it's registered.
256 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
279 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
257 commentFormGlobalSubmitSuccessCallback()
280 commentFormGlobalSubmitSuccessCallback()
258 }
281 }
259 };
282 };
260
283
261 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
284 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
262 failHandler = failHandler || function() {};
285 return _submitAjaxPOST(url, postData, successHandler, failHandler);
263 var postData = toQueryString(postData);
264 var request = $.ajax({
265 url: url,
266 type: 'POST',
267 data: postData,
268 headers: {'X-PARTIAL-XHR': true}
269 })
270 .done(function(data) {
271 successHandler(data);
272 })
273 .fail(function(data, textStatus, errorThrown){
274 alert(
275 "Error while submitting comment.\n" +
276 "Error code {0} ({1}).".format(data.status, data.statusText));
277 failHandler()
278 });
279 return request;
280 };
286 };
281
287
282 // overwrite a submitHandler, we need to do it for inline comments
288 // overwrite a submitHandler, we need to do it for inline comments
283 this.setHandleFormSubmit = function(callback) {
289 this.setHandleFormSubmit = function(callback) {
284 this.handleFormSubmit = callback;
290 this.handleFormSubmit = callback;
285 };
291 };
286
292
287 // overwrite a submitSuccessHandler
293 // overwrite a submitSuccessHandler
288 this.setGlobalSubmitSuccessCallback = function(callback) {
294 this.setGlobalSubmitSuccessCallback = function(callback) {
289 this.globalSubmitSuccessCallback = callback;
295 this.globalSubmitSuccessCallback = callback;
290 };
296 };
291
297
292 // default handler for for submit for main comments
298 // default handler for for submit for main comments
293 this.handleFormSubmit = function() {
299 this.handleFormSubmit = function() {
294 var text = self.cm.getValue();
300 var text = self.cm.getValue();
295 var status = self.getCommentStatus();
301 var status = self.getCommentStatus();
296 var commentType = self.getCommentType();
302 var commentType = self.getCommentType();
297 var resolvesCommentId = self.getResolvesId();
303 var resolvesCommentId = self.getResolvesId();
298 var closePullRequest = self.getClosePr();
304 var closePullRequest = self.getClosePr();
299
305
300 if (text === "" && !status) {
306 if (text === "" && !status) {
301 return;
307 return;
302 }
308 }
303
309
304 var excludeCancelBtn = false;
310 var excludeCancelBtn = false;
305 var submitEvent = true;
311 var submitEvent = true;
306 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
312 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
307 self.cm.setOption("readOnly", true);
313 self.cm.setOption("readOnly", true);
308
314
309 var postData = {
315 var postData = {
310 'text': text,
316 'text': text,
311 'changeset_status': status,
317 'changeset_status': status,
312 'comment_type': commentType,
318 'comment_type': commentType,
313 'csrf_token': CSRF_TOKEN
319 'csrf_token': CSRF_TOKEN
314 };
320 };
315
321
316 if (resolvesCommentId) {
322 if (resolvesCommentId) {
317 postData['resolves_comment_id'] = resolvesCommentId;
323 postData['resolves_comment_id'] = resolvesCommentId;
318 }
324 }
319
325
320 if (closePullRequest) {
326 if (closePullRequest) {
321 postData['close_pull_request'] = true;
327 postData['close_pull_request'] = true;
322 }
328 }
323
329
324 var submitSuccessCallback = function(o) {
330 var submitSuccessCallback = function(o) {
325 // reload page if we change status for single commit.
331 // reload page if we change status for single commit.
326 if (status && self.commitId) {
332 if (status && self.commitId) {
327 location.reload(true);
333 location.reload(true);
328 } else {
334 } else {
329 $('#injected_page_comments').append(o.rendered_text);
335 $('#injected_page_comments').append(o.rendered_text);
330 self.resetCommentFormState();
336 self.resetCommentFormState();
331 timeagoActivate();
337 timeagoActivate();
332
338
333 // mark visually which comment was resolved
339 // mark visually which comment was resolved
334 if (resolvesCommentId) {
340 if (resolvesCommentId) {
335 self.markCommentResolved(resolvesCommentId);
341 self.markCommentResolved(resolvesCommentId);
336 }
342 }
337 }
343 }
338
344
339 // run global callback on submit
345 // run global callback on submit
340 self.globalSubmitSuccessCallback();
346 self.globalSubmitSuccessCallback();
341
347
342 };
348 };
343 var submitFailCallback = function(){
349 var submitFailCallback = function(data) {
350 alert(
351 "Error while submitting comment.\n" +
352 "Error code {0} ({1}).".format(data.status, data.statusText)
353 );
344 self.resetCommentFormState(text);
354 self.resetCommentFormState(text);
345 };
355 };
346 self.submitAjaxPOST(
356 self.submitAjaxPOST(
347 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
357 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
348 };
358 };
349
359
350 this.previewSuccessCallback = function(o) {
360 this.previewSuccessCallback = function(o) {
351 $(self.previewBoxSelector).html(o);
361 $(self.previewBoxSelector).html(o);
352 $(self.previewBoxSelector).removeClass('unloaded');
362 $(self.previewBoxSelector).removeClass('unloaded');
353
363
354 // swap buttons, making preview active
364 // swap buttons, making preview active
355 $(self.previewButton).parent().addClass('active');
365 $(self.previewButton).parent().addClass('active');
356 $(self.editButton).parent().removeClass('active');
366 $(self.editButton).parent().removeClass('active');
357
367
358 // unlock buttons
368 // unlock buttons
359 self.setActionButtonsDisabled(false);
369 self.setActionButtonsDisabled(false);
360 };
370 };
361
371
362 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
372 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
363 excludeCancelBtn = excludeCancelBtn || false;
373 excludeCancelBtn = excludeCancelBtn || false;
364 submitEvent = submitEvent || false;
374 submitEvent = submitEvent || false;
365
375
366 $(this.editButton).prop('disabled', state);
376 $(this.editButton).prop('disabled', state);
367 $(this.previewButton).prop('disabled', state);
377 $(this.previewButton).prop('disabled', state);
368
378
369 if (!excludeCancelBtn) {
379 if (!excludeCancelBtn) {
370 $(this.cancelButton).prop('disabled', state);
380 $(this.cancelButton).prop('disabled', state);
371 }
381 }
372
382
373 var submitState = state;
383 var submitState = state;
374 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
384 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
375 // if the value of commit review status is set, we allow
385 // if the value of commit review status is set, we allow
376 // submit button, but only on Main form, isInline means inline
386 // submit button, but only on Main form, isInline means inline
377 submitState = false
387 submitState = false
378 }
388 }
379
389
380 $(this.submitButton).prop('disabled', submitState);
390 $(this.submitButton).prop('disabled', submitState);
381 if (submitEvent) {
391 if (submitEvent) {
382 $(this.submitButton).val(_gettext('Submitting...'));
392 $(this.submitButton).val(_gettext('Submitting...'));
383 } else {
393 } else {
384 $(this.submitButton).val(this.submitButtonText);
394 $(this.submitButton).val(this.submitButtonText);
385 }
395 }
386
396
387 };
397 };
388
398
389 // lock preview/edit/submit buttons on load, but exclude cancel button
399 // lock preview/edit/submit buttons on load, but exclude cancel button
390 var excludeCancelBtn = true;
400 var excludeCancelBtn = true;
391 this.setActionButtonsDisabled(true, excludeCancelBtn);
401 this.setActionButtonsDisabled(true, excludeCancelBtn);
392
402
393 // anonymous users don't have access to initialized CM instance
403 // anonymous users don't have access to initialized CM instance
394 if (this.cm !== undefined){
404 if (this.cm !== undefined){
395 this.cm.on('change', function(cMirror) {
405 this.cm.on('change', function(cMirror) {
396 if (cMirror.getValue() === "") {
406 if (cMirror.getValue() === "") {
397 self.setActionButtonsDisabled(true, excludeCancelBtn)
407 self.setActionButtonsDisabled(true, excludeCancelBtn)
398 } else {
408 } else {
399 self.setActionButtonsDisabled(false, excludeCancelBtn)
409 self.setActionButtonsDisabled(false, excludeCancelBtn)
400 }
410 }
401 });
411 });
402 }
412 }
403
413
404 $(this.editButton).on('click', function(e) {
414 $(this.editButton).on('click', function(e) {
405 e.preventDefault();
415 e.preventDefault();
406
416
407 $(self.previewButton).parent().removeClass('active');
417 $(self.previewButton).parent().removeClass('active');
408 $(self.previewContainer).hide();
418 $(self.previewContainer).hide();
409
419
410 $(self.editButton).parent().addClass('active');
420 $(self.editButton).parent().addClass('active');
411 $(self.editContainer).show();
421 $(self.editContainer).show();
412
422
413 });
423 });
414
424
415 $(this.previewButton).on('click', function(e) {
425 $(this.previewButton).on('click', function(e) {
416 e.preventDefault();
426 e.preventDefault();
417 var text = self.cm.getValue();
427 var text = self.cm.getValue();
418
428
419 if (text === "") {
429 if (text === "") {
420 return;
430 return;
421 }
431 }
422
432
423 var postData = {
433 var postData = {
424 'text': text,
434 'text': text,
425 'renderer': templateContext.visual.default_renderer,
435 'renderer': templateContext.visual.default_renderer,
426 'csrf_token': CSRF_TOKEN
436 'csrf_token': CSRF_TOKEN
427 };
437 };
428
438
429 // lock ALL buttons on preview
439 // lock ALL buttons on preview
430 self.setActionButtonsDisabled(true);
440 self.setActionButtonsDisabled(true);
431
441
432 $(self.previewBoxSelector).addClass('unloaded');
442 $(self.previewBoxSelector).addClass('unloaded');
433 $(self.previewBoxSelector).html(_gettext('Loading ...'));
443 $(self.previewBoxSelector).html(_gettext('Loading ...'));
434
444
435 $(self.editContainer).hide();
445 $(self.editContainer).hide();
436 $(self.previewContainer).show();
446 $(self.previewContainer).show();
437
447
438 // by default we reset state of comment preserving the text
448 // by default we reset state of comment preserving the text
439 var previewFailCallback = function(){
449 var previewFailCallback = function(data){
450 alert(
451 "Error while preview of comment.\n" +
452 "Error code {0} ({1}).".format(data.status, data.statusText)
453 );
440 self.resetCommentFormState(text)
454 self.resetCommentFormState(text)
441 };
455 };
442 self.submitAjaxPOST(
456 self.submitAjaxPOST(
443 self.previewUrl, postData, self.previewSuccessCallback,
457 self.previewUrl, postData, self.previewSuccessCallback,
444 previewFailCallback);
458 previewFailCallback);
445
459
446 $(self.previewButton).parent().addClass('active');
460 $(self.previewButton).parent().addClass('active');
447 $(self.editButton).parent().removeClass('active');
461 $(self.editButton).parent().removeClass('active');
448 });
462 });
449
463
450 $(this.submitForm).submit(function(e) {
464 $(this.submitForm).submit(function(e) {
451 e.preventDefault();
465 e.preventDefault();
452 var allowedToSubmit = self.isAllowedToSubmit();
466 var allowedToSubmit = self.isAllowedToSubmit();
453 if (!allowedToSubmit){
467 if (!allowedToSubmit){
454 return false;
468 return false;
455 }
469 }
456 self.handleFormSubmit();
470 self.handleFormSubmit();
457 });
471 });
458
472
459 }
473 }
460
474
461 return CommentForm;
475 return CommentForm;
462 });
476 });
463
477
464 /* comments controller */
478 /* comments controller */
465 var CommentsController = function() {
479 var CommentsController = function() {
466 var mainComment = '#text';
480 var mainComment = '#text';
467 var self = this;
481 var self = this;
468
482
469 this.cancelComment = function(node) {
483 this.cancelComment = function(node) {
470 var $node = $(node);
484 var $node = $(node);
471 var $td = $node.closest('td');
485 var $td = $node.closest('td');
472 $node.closest('.comment-inline-form').remove();
486 $node.closest('.comment-inline-form').remove();
473 return false;
487 return false;
474 };
488 };
475
489
476 this.getLineNumber = function(node) {
490 this.getLineNumber = function(node) {
477 var $node = $(node);
491 var $node = $(node);
478 var lineNo = $node.closest('td').attr('data-line-no');
492 var lineNo = $node.closest('td').attr('data-line-no');
479 if (lineNo === undefined && $node.data('commentInline')){
493 if (lineNo === undefined && $node.data('commentInline')){
480 lineNo = $node.data('commentLineNo')
494 lineNo = $node.data('commentLineNo')
481 }
495 }
482
496
483 return lineNo
497 return lineNo
484 };
498 };
485
499
486 this.scrollToComment = function(node, offset, outdated) {
500 this.scrollToComment = function(node, offset, outdated) {
487 if (offset === undefined) {
501 if (offset === undefined) {
488 offset = 0;
502 offset = 0;
489 }
503 }
490 var outdated = outdated || false;
504 var outdated = outdated || false;
491 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
505 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
492
506
493 if (!node) {
507 if (!node) {
494 node = $('.comment-selected');
508 node = $('.comment-selected');
495 if (!node.length) {
509 if (!node.length) {
496 node = $('comment-current')
510 node = $('comment-current')
497 }
511 }
498 }
512 }
499 $wrapper = $(node).closest('div.comment');
513 $wrapper = $(node).closest('div.comment');
500 $comment = $(node).closest(klass);
514 $comment = $(node).closest(klass);
501 $comments = $(klass);
515 $comments = $(klass);
502
516
503 // show hidden comment when referenced.
517 // show hidden comment when referenced.
504 if (!$wrapper.is(':visible')){
518 if (!$wrapper.is(':visible')){
505 $wrapper.show();
519 $wrapper.show();
506 }
520 }
507
521
508 $('.comment-selected').removeClass('comment-selected');
522 $('.comment-selected').removeClass('comment-selected');
509
523
510 var nextIdx = $(klass).index($comment) + offset;
524 var nextIdx = $(klass).index($comment) + offset;
511 if (nextIdx >= $comments.length) {
525 if (nextIdx >= $comments.length) {
512 nextIdx = 0;
526 nextIdx = 0;
513 }
527 }
514 var $next = $(klass).eq(nextIdx);
528 var $next = $(klass).eq(nextIdx);
515
529
516 var $cb = $next.closest('.cb');
530 var $cb = $next.closest('.cb');
517 $cb.removeClass('cb-collapsed');
531 $cb.removeClass('cb-collapsed');
518
532
519 var $filediffCollapseState = $cb.closest('.filediff').prev();
533 var $filediffCollapseState = $cb.closest('.filediff').prev();
520 $filediffCollapseState.prop('checked', false);
534 $filediffCollapseState.prop('checked', false);
521 $next.addClass('comment-selected');
535 $next.addClass('comment-selected');
522 scrollToElement($next);
536 scrollToElement($next);
523 return false;
537 return false;
524 };
538 };
525
539
526 this.nextComment = function(node) {
540 this.nextComment = function(node) {
527 return self.scrollToComment(node, 1);
541 return self.scrollToComment(node, 1);
528 };
542 };
529
543
530 this.prevComment = function(node) {
544 this.prevComment = function(node) {
531 return self.scrollToComment(node, -1);
545 return self.scrollToComment(node, -1);
532 };
546 };
533
547
534 this.nextOutdatedComment = function(node) {
548 this.nextOutdatedComment = function(node) {
535 return self.scrollToComment(node, 1, true);
549 return self.scrollToComment(node, 1, true);
536 };
550 };
537
551
538 this.prevOutdatedComment = function(node) {
552 this.prevOutdatedComment = function(node) {
539 return self.scrollToComment(node, -1, true);
553 return self.scrollToComment(node, -1, true);
540 };
554 };
541
555
542 this.deleteComment = function(node) {
556 this.deleteComment = function(node) {
543 if (!confirm(_gettext('Delete this comment?'))) {
557 if (!confirm(_gettext('Delete this comment?'))) {
544 return false;
558 return false;
545 }
559 }
546 var $node = $(node);
560 var $node = $(node);
547 var $td = $node.closest('td');
561 var $td = $node.closest('td');
548 var $comment = $node.closest('.comment');
562 var $comment = $node.closest('.comment');
549 var comment_id = $comment.attr('data-comment-id');
563 var comment_id = $comment.attr('data-comment-id');
550 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
564 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
551 var postData = {
565 var postData = {
552 'csrf_token': CSRF_TOKEN
566 'csrf_token': CSRF_TOKEN
553 };
567 };
554
568
555 $comment.addClass('comment-deleting');
569 $comment.addClass('comment-deleting');
556 $comment.hide('fast');
570 $comment.hide('fast');
557
571
558 var success = function(response) {
572 var success = function(response) {
559 $comment.remove();
573 $comment.remove();
560 return false;
574 return false;
561 };
575 };
562 var failure = function(data, textStatus, xhr) {
576 var failure = function(data, textStatus, xhr) {
563 alert("error processing request: " + textStatus);
577 alert("error processing request: " + textStatus);
564 $comment.show('fast');
578 $comment.show('fast');
565 $comment.removeClass('comment-deleting');
579 $comment.removeClass('comment-deleting');
566 return false;
580 return false;
567 };
581 };
568 ajaxPOST(url, postData, success, failure);
582 ajaxPOST(url, postData, success, failure);
569 };
583 };
570
584
571 this.toggleWideMode = function (node) {
585 this.toggleWideMode = function (node) {
572 if ($('#content').hasClass('wrapper')) {
586 if ($('#content').hasClass('wrapper')) {
573 $('#content').removeClass("wrapper");
587 $('#content').removeClass("wrapper");
574 $('#content').addClass("wide-mode-wrapper");
588 $('#content').addClass("wide-mode-wrapper");
575 $(node).addClass('btn-success');
589 $(node).addClass('btn-success');
576 } else {
590 } else {
577 $('#content').removeClass("wide-mode-wrapper");
591 $('#content').removeClass("wide-mode-wrapper");
578 $('#content').addClass("wrapper");
592 $('#content').addClass("wrapper");
579 $(node).removeClass('btn-success');
593 $(node).removeClass('btn-success');
580 }
594 }
581 return false;
595 return false;
582 };
596 };
583
597
584 this.toggleComments = function(node, show) {
598 this.toggleComments = function(node, show) {
585 var $filediff = $(node).closest('.filediff');
599 var $filediff = $(node).closest('.filediff');
586 if (show === true) {
600 if (show === true) {
587 $filediff.removeClass('hide-comments');
601 $filediff.removeClass('hide-comments');
588 } else if (show === false) {
602 } else if (show === false) {
589 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
603 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
590 $filediff.addClass('hide-comments');
604 $filediff.addClass('hide-comments');
591 } else {
605 } else {
592 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
606 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
593 $filediff.toggleClass('hide-comments');
607 $filediff.toggleClass('hide-comments');
594 }
608 }
595 return false;
609 return false;
596 };
610 };
597
611
598 this.toggleLineComments = function(node) {
612 this.toggleLineComments = function(node) {
599 self.toggleComments(node, true);
613 self.toggleComments(node, true);
600 var $node = $(node);
614 var $node = $(node);
601 // mark outdated comments as visible before the toggle;
615 // mark outdated comments as visible before the toggle;
602 $(node.closest('tr')).find('.comment-outdated').show();
616 $(node.closest('tr')).find('.comment-outdated').show();
603 $node.closest('tr').toggleClass('hide-line-comments');
617 $node.closest('tr').toggleClass('hide-line-comments');
604 };
618 };
605
619
606 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId){
620 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId){
607 var pullRequestId = templateContext.pull_request_data.pull_request_id;
621 var pullRequestId = templateContext.pull_request_data.pull_request_id;
608 var commitId = templateContext.commit_data.commit_id;
622 var commitId = templateContext.commit_data.commit_id;
609
623
610 var commentForm = new CommentForm(
624 var commentForm = new CommentForm(
611 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId);
625 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId);
612 var cm = commentForm.getCmInstance();
626 var cm = commentForm.getCmInstance();
613
627
614 if (resolvesCommentId){
628 if (resolvesCommentId){
615 var placeholderText = _gettext('Leave a comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
629 var placeholderText = _gettext('Leave a comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
616 }
630 }
617
631
618 setTimeout(function() {
632 setTimeout(function() {
619 // callbacks
633 // callbacks
620 if (cm !== undefined) {
634 if (cm !== undefined) {
621 commentForm.setPlaceholder(placeholderText);
635 commentForm.setPlaceholder(placeholderText);
622 if (commentForm.isInline()) {
636 if (commentForm.isInline()) {
623 cm.focus();
637 cm.focus();
624 cm.refresh();
638 cm.refresh();
625 }
639 }
626 }
640 }
627 }, 10);
641 }, 10);
628
642
629 // trigger scrolldown to the resolve comment, since it might be away
643 // trigger scrolldown to the resolve comment, since it might be away
630 // from the clicked
644 // from the clicked
631 if (resolvesCommentId){
645 if (resolvesCommentId){
632 var actionNode = $(commentForm.resolvesActionId).offset();
646 var actionNode = $(commentForm.resolvesActionId).offset();
633
647
634 setTimeout(function() {
648 setTimeout(function() {
635 if (actionNode) {
649 if (actionNode) {
636 $('body, html').animate({scrollTop: actionNode.top}, 10);
650 $('body, html').animate({scrollTop: actionNode.top}, 10);
637 }
651 }
638 }, 100);
652 }, 100);
639 }
653 }
640
654
641 return commentForm;
655 return commentForm;
642 };
656 };
643
657
644 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
658 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
645
659
646 var tmpl = $('#cb-comment-general-form-template').html();
660 var tmpl = $('#cb-comment-general-form-template').html();
647 tmpl = tmpl.format(null, 'general');
661 tmpl = tmpl.format(null, 'general');
648 var $form = $(tmpl);
662 var $form = $(tmpl);
649
663
650 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
664 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
651 var curForm = $formPlaceholder.find('form');
665 var curForm = $formPlaceholder.find('form');
652 if (curForm){
666 if (curForm){
653 curForm.remove();
667 curForm.remove();
654 }
668 }
655 $formPlaceholder.append($form);
669 $formPlaceholder.append($form);
656
670
657 var _form = $($form[0]);
671 var _form = $($form[0]);
658 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
672 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
659 var commentForm = this.createCommentForm(
673 var commentForm = this.createCommentForm(
660 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId);
674 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId);
661 commentForm.initStatusChangeSelector();
675 commentForm.initStatusChangeSelector();
662
676
663 return commentForm;
677 return commentForm;
664 };
678 };
665
679
666 this.createComment = function(node, resolutionComment) {
680 this.createComment = function(node, resolutionComment) {
667 var resolvesCommentId = resolutionComment || null;
681 var resolvesCommentId = resolutionComment || null;
668 var $node = $(node);
682 var $node = $(node);
669 var $td = $node.closest('td');
683 var $td = $node.closest('td');
670 var $form = $td.find('.comment-inline-form');
684 var $form = $td.find('.comment-inline-form');
671
685
672 if (!$form.length) {
686 if (!$form.length) {
673
687
674 var $filediff = $node.closest('.filediff');
688 var $filediff = $node.closest('.filediff');
675 $filediff.removeClass('hide-comments');
689 $filediff.removeClass('hide-comments');
676 var f_path = $filediff.attr('data-f-path');
690 var f_path = $filediff.attr('data-f-path');
677 var lineno = self.getLineNumber(node);
691 var lineno = self.getLineNumber(node);
678 // create a new HTML from template
692 // create a new HTML from template
679 var tmpl = $('#cb-comment-inline-form-template').html();
693 var tmpl = $('#cb-comment-inline-form-template').html();
680 tmpl = tmpl.format(escapeHtml(f_path), lineno);
694 tmpl = tmpl.format(escapeHtml(f_path), lineno);
681 $form = $(tmpl);
695 $form = $(tmpl);
682
696
683 var $comments = $td.find('.inline-comments');
697 var $comments = $td.find('.inline-comments');
684 if (!$comments.length) {
698 if (!$comments.length) {
685 $comments = $(
699 $comments = $(
686 $('#cb-comments-inline-container-template').html());
700 $('#cb-comments-inline-container-template').html());
687 $td.append($comments);
701 $td.append($comments);
688 }
702 }
689
703
690 $td.find('.cb-comment-add-button').before($form);
704 $td.find('.cb-comment-add-button').before($form);
691
705
692 var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno);
706 var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno);
693 var _form = $($form[0]).find('form');
707 var _form = $($form[0]).find('form');
694 var autocompleteActions = ['as_note', 'as_todo'];
708 var autocompleteActions = ['as_note', 'as_todo'];
695 var commentForm = this.createCommentForm(
709 var commentForm = this.createCommentForm(
696 _form, lineno, placeholderText, autocompleteActions, resolvesCommentId);
710 _form, lineno, placeholderText, autocompleteActions, resolvesCommentId);
697
711
698 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
712 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
699 form: _form,
713 form: _form,
700 parent: $td[0],
714 parent: $td[0],
701 lineno: lineno,
715 lineno: lineno,
702 f_path: f_path}
716 f_path: f_path}
703 );
717 );
704
718
705 // set a CUSTOM submit handler for inline comments.
719 // set a CUSTOM submit handler for inline comments.
706 commentForm.setHandleFormSubmit(function(o) {
720 commentForm.setHandleFormSubmit(function(o) {
707 var text = commentForm.cm.getValue();
721 var text = commentForm.cm.getValue();
708 var commentType = commentForm.getCommentType();
722 var commentType = commentForm.getCommentType();
709 var resolvesCommentId = commentForm.getResolvesId();
723 var resolvesCommentId = commentForm.getResolvesId();
710
724
711 if (text === "") {
725 if (text === "") {
712 return;
726 return;
713 }
727 }
714
728
715 if (lineno === undefined) {
729 if (lineno === undefined) {
716 alert('missing line !');
730 alert('missing line !');
717 return;
731 return;
718 }
732 }
719 if (f_path === undefined) {
733 if (f_path === undefined) {
720 alert('missing file path !');
734 alert('missing file path !');
721 return;
735 return;
722 }
736 }
723
737
724 var excludeCancelBtn = false;
738 var excludeCancelBtn = false;
725 var submitEvent = true;
739 var submitEvent = true;
726 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
740 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
727 commentForm.cm.setOption("readOnly", true);
741 commentForm.cm.setOption("readOnly", true);
728 var postData = {
742 var postData = {
729 'text': text,
743 'text': text,
730 'f_path': f_path,
744 'f_path': f_path,
731 'line': lineno,
745 'line': lineno,
732 'comment_type': commentType,
746 'comment_type': commentType,
733 'csrf_token': CSRF_TOKEN
747 'csrf_token': CSRF_TOKEN
734 };
748 };
735 if (resolvesCommentId){
749 if (resolvesCommentId){
736 postData['resolves_comment_id'] = resolvesCommentId;
750 postData['resolves_comment_id'] = resolvesCommentId;
737 }
751 }
738
752
739 var submitSuccessCallback = function(json_data) {
753 var submitSuccessCallback = function(json_data) {
740 $form.remove();
754 $form.remove();
741 try {
755 try {
742 var html = json_data.rendered_text;
756 var html = json_data.rendered_text;
743 var lineno = json_data.line_no;
757 var lineno = json_data.line_no;
744 var target_id = json_data.target_id;
758 var target_id = json_data.target_id;
745
759
746 $comments.find('.cb-comment-add-button').before(html);
760 $comments.find('.cb-comment-add-button').before(html);
747
761
748 //mark visually which comment was resolved
762 //mark visually which comment was resolved
749 if (resolvesCommentId) {
763 if (resolvesCommentId) {
750 commentForm.markCommentResolved(resolvesCommentId);
764 commentForm.markCommentResolved(resolvesCommentId);
751 }
765 }
752
766
753 // run global callback on submit
767 // run global callback on submit
754 commentForm.globalSubmitSuccessCallback();
768 commentForm.globalSubmitSuccessCallback();
755
769
756 } catch (e) {
770 } catch (e) {
757 console.error(e);
771 console.error(e);
758 }
772 }
759
773
760 // re trigger the linkification of next/prev navigation
774 // re trigger the linkification of next/prev navigation
761 linkifyComments($('.inline-comment-injected'));
775 linkifyComments($('.inline-comment-injected'));
762 timeagoActivate();
776 timeagoActivate();
763 commentForm.setActionButtonsDisabled(false);
777 commentForm.setActionButtonsDisabled(false);
764
778
765 };
779 };
766 var submitFailCallback = function(){
780 var submitFailCallback = function(data){
781 alert(
782 "Error while submitting comment.\n" +
783 "Error code {0} ({1}).".format(data.status, data.statusText)
784 );
767 commentForm.resetCommentFormState(text)
785 commentForm.resetCommentFormState(text)
768 };
786 };
769 commentForm.submitAjaxPOST(
787 commentForm.submitAjaxPOST(
770 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
788 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
771 });
789 });
772 }
790 }
773
791
774 $form.addClass('comment-inline-form-open');
792 $form.addClass('comment-inline-form-open');
775 };
793 };
776
794
777 this.createResolutionComment = function(commentId){
795 this.createResolutionComment = function(commentId){
778 // hide the trigger text
796 // hide the trigger text
779 $('#resolve-comment-{0}'.format(commentId)).hide();
797 $('#resolve-comment-{0}'.format(commentId)).hide();
780
798
781 var comment = $('#comment-'+commentId);
799 var comment = $('#comment-'+commentId);
782 var commentData = comment.data();
800 var commentData = comment.data();
783 if (commentData.commentInline) {
801 if (commentData.commentInline) {
784 this.createComment(comment, commentId)
802 this.createComment(comment, commentId)
785 } else {
803 } else {
786 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
804 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
787 }
805 }
788
806
789 return false;
807 return false;
790 };
808 };
791
809
792 this.submitResolution = function(commentId){
810 this.submitResolution = function(commentId){
793 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
811 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
794 var commentForm = form.get(0).CommentForm;
812 var commentForm = form.get(0).CommentForm;
795
813
796 var cm = commentForm.getCmInstance();
814 var cm = commentForm.getCmInstance();
797 var renderer = templateContext.visual.default_renderer;
815 var renderer = templateContext.visual.default_renderer;
798 if (renderer == 'rst'){
816 if (renderer == 'rst'){
799 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
817 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
800 } else if (renderer == 'markdown') {
818 } else if (renderer == 'markdown') {
801 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
819 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
802 } else {
820 } else {
803 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
821 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
804 }
822 }
805
823
806 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
824 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
807 form.submit();
825 form.submit();
808 return false;
826 return false;
809 };
827 };
810
828
811 };
829 };
@@ -1,593 +1,550 b''
1 // # Copyright (C) 2010-2018 RhodeCode GmbH
1 // # Copyright (C) 2010-2018 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19
19
20 var prButtonLockChecks = {
20 var prButtonLockChecks = {
21 'compare': false,
21 'compare': false,
22 'reviewers': false
22 'reviewers': false
23 };
23 };
24
24
25 /**
25 /**
26 * lock button until all checks and loads are made. E.g reviewer calculation
26 * lock button until all checks and loads are made. E.g reviewer calculation
27 * should prevent from submitting a PR
27 * should prevent from submitting a PR
28 * @param lockEnabled
28 * @param lockEnabled
29 * @param msg
29 * @param msg
30 * @param scope
30 * @param scope
31 */
31 */
32 var prButtonLock = function(lockEnabled, msg, scope) {
32 var prButtonLock = function(lockEnabled, msg, scope) {
33 scope = scope || 'all';
33 scope = scope || 'all';
34 if (scope == 'all'){
34 if (scope == 'all'){
35 prButtonLockChecks['compare'] = !lockEnabled;
35 prButtonLockChecks['compare'] = !lockEnabled;
36 prButtonLockChecks['reviewers'] = !lockEnabled;
36 prButtonLockChecks['reviewers'] = !lockEnabled;
37 } else if (scope == 'compare') {
37 } else if (scope == 'compare') {
38 prButtonLockChecks['compare'] = !lockEnabled;
38 prButtonLockChecks['compare'] = !lockEnabled;
39 } else if (scope == 'reviewers'){
39 } else if (scope == 'reviewers'){
40 prButtonLockChecks['reviewers'] = !lockEnabled;
40 prButtonLockChecks['reviewers'] = !lockEnabled;
41 }
41 }
42 var checksMeet = prButtonLockChecks.compare && prButtonLockChecks.reviewers;
42 var checksMeet = prButtonLockChecks.compare && prButtonLockChecks.reviewers;
43 if (lockEnabled) {
43 if (lockEnabled) {
44 $('#pr_submit').attr('disabled', 'disabled');
44 $('#pr_submit').attr('disabled', 'disabled');
45 }
45 }
46 else if (checksMeet) {
46 else if (checksMeet) {
47 $('#pr_submit').removeAttr('disabled');
47 $('#pr_submit').removeAttr('disabled');
48 }
48 }
49
49
50 if (msg) {
50 if (msg) {
51 $('#pr_open_message').html(msg);
51 $('#pr_open_message').html(msg);
52 }
52 }
53 };
53 };
54
54
55
55
56 /**
56 /**
57 Generate Title and Description for a PullRequest.
57 Generate Title and Description for a PullRequest.
58 In case of 1 commits, the title and description is that one commit
58 In case of 1 commits, the title and description is that one commit
59 in case of multiple commits, we iterate on them with max N number of commits,
59 in case of multiple commits, we iterate on them with max N number of commits,
60 and build description in a form
60 and build description in a form
61 - commitN
61 - commitN
62 - commitN+1
62 - commitN+1
63 ...
63 ...
64
64
65 Title is then constructed from branch names, or other references,
65 Title is then constructed from branch names, or other references,
66 replacing '-' and '_' into spaces
66 replacing '-' and '_' into spaces
67
67
68 * @param sourceRef
68 * @param sourceRef
69 * @param elements
69 * @param elements
70 * @param limit
70 * @param limit
71 * @returns {*[]}
71 * @returns {*[]}
72 */
72 */
73 var getTitleAndDescription = function(sourceRef, elements, limit) {
73 var getTitleAndDescription = function(sourceRef, elements, limit) {
74 var title = '';
74 var title = '';
75 var desc = '';
75 var desc = '';
76
76
77 $.each($(elements).get().reverse().slice(0, limit), function(idx, value) {
77 $.each($(elements).get().reverse().slice(0, limit), function(idx, value) {
78 var rawMessage = $(value).find('td.td-description .message').data('messageRaw');
78 var rawMessage = $(value).find('td.td-description .message').data('messageRaw');
79 desc += '- ' + rawMessage.split('\n')[0].replace(/\n+$/, "") + '\n';
79 desc += '- ' + rawMessage.split('\n')[0].replace(/\n+$/, "") + '\n';
80 });
80 });
81 // only 1 commit, use commit message as title
81 // only 1 commit, use commit message as title
82 if (elements.length === 1) {
82 if (elements.length === 1) {
83 title = $(elements[0]).find('td.td-description .message').data('messageRaw').split('\n')[0];
83 title = $(elements[0]).find('td.td-description .message').data('messageRaw').split('\n')[0];
84 }
84 }
85 else {
85 else {
86 // use reference name
86 // use reference name
87 title = sourceRef.replace(/-/g, ' ').replace(/_/g, ' ').capitalizeFirstLetter();
87 title = sourceRef.replace(/-/g, ' ').replace(/_/g, ' ').capitalizeFirstLetter();
88 }
88 }
89
89
90 return [title, desc]
90 return [title, desc]
91 };
91 };
92
92
93
93
94
94
95 ReviewersController = function () {
95 ReviewersController = function () {
96 var self = this;
96 var self = this;
97 this.$reviewRulesContainer = $('#review_rules');
97 this.$reviewRulesContainer = $('#review_rules');
98 this.$rulesList = this.$reviewRulesContainer.find('.pr-reviewer-rules');
98 this.$rulesList = this.$reviewRulesContainer.find('.pr-reviewer-rules');
99 this.forbidReviewUsers = undefined;
99 this.forbidReviewUsers = undefined;
100 this.$reviewMembers = $('#review_members');
100 this.$reviewMembers = $('#review_members');
101 this.currentRequest = null;
101 this.currentRequest = null;
102
102
103 this.defaultForbidReviewUsers = function() {
103 this.defaultForbidReviewUsers = function() {
104 return [
104 return [
105 {'username': 'default',
105 {'username': 'default',
106 'user_id': templateContext.default_user.user_id}
106 'user_id': templateContext.default_user.user_id}
107 ];
107 ];
108 };
108 };
109
109
110 this.hideReviewRules = function() {
110 this.hideReviewRules = function() {
111 self.$reviewRulesContainer.hide();
111 self.$reviewRulesContainer.hide();
112 };
112 };
113
113
114 this.showReviewRules = function() {
114 this.showReviewRules = function() {
115 self.$reviewRulesContainer.show();
115 self.$reviewRulesContainer.show();
116 };
116 };
117
117
118 this.addRule = function(ruleText) {
118 this.addRule = function(ruleText) {
119 self.showReviewRules();
119 self.showReviewRules();
120 return '<div>- {0}</div>'.format(ruleText)
120 return '<div>- {0}</div>'.format(ruleText)
121 };
121 };
122
122
123 this.loadReviewRules = function(data) {
123 this.loadReviewRules = function(data) {
124 // reset forbidden Users
124 // reset forbidden Users
125 this.forbidReviewUsers = self.defaultForbidReviewUsers();
125 this.forbidReviewUsers = self.defaultForbidReviewUsers();
126
126
127 // reset state of review rules
127 // reset state of review rules
128 self.$rulesList.html('');
128 self.$rulesList.html('');
129
129
130 if (!data || data.rules === undefined || $.isEmptyObject(data.rules)) {
130 if (!data || data.rules === undefined || $.isEmptyObject(data.rules)) {
131 // default rule, case for older repo that don't have any rules stored
131 // default rule, case for older repo that don't have any rules stored
132 self.$rulesList.append(
132 self.$rulesList.append(
133 self.addRule(
133 self.addRule(
134 _gettext('All reviewers must vote.'))
134 _gettext('All reviewers must vote.'))
135 );
135 );
136 return self.forbidReviewUsers
136 return self.forbidReviewUsers
137 }
137 }
138
138
139 if (data.rules.voting !== undefined) {
139 if (data.rules.voting !== undefined) {
140 if (data.rules.voting < 0) {
140 if (data.rules.voting < 0) {
141 self.$rulesList.append(
141 self.$rulesList.append(
142 self.addRule(
142 self.addRule(
143 _gettext('All individual reviewers must vote.'))
143 _gettext('All individual reviewers must vote.'))
144 )
144 )
145 } else if (data.rules.voting === 1) {
145 } else if (data.rules.voting === 1) {
146 self.$rulesList.append(
146 self.$rulesList.append(
147 self.addRule(
147 self.addRule(
148 _gettext('At least {0} reviewer must vote.').format(data.rules.voting))
148 _gettext('At least {0} reviewer must vote.').format(data.rules.voting))
149 )
149 )
150
150
151 } else {
151 } else {
152 self.$rulesList.append(
152 self.$rulesList.append(
153 self.addRule(
153 self.addRule(
154 _gettext('At least {0} reviewers must vote.').format(data.rules.voting))
154 _gettext('At least {0} reviewers must vote.').format(data.rules.voting))
155 )
155 )
156 }
156 }
157 }
157 }
158
158
159 if (data.rules.voting_groups !== undefined) {
159 if (data.rules.voting_groups !== undefined) {
160 $.each(data.rules.voting_groups, function(index, rule_data) {
160 $.each(data.rules.voting_groups, function(index, rule_data) {
161 self.$rulesList.append(
161 self.$rulesList.append(
162 self.addRule(rule_data.text)
162 self.addRule(rule_data.text)
163 )
163 )
164 });
164 });
165 }
165 }
166
166
167 if (data.rules.use_code_authors_for_review) {
167 if (data.rules.use_code_authors_for_review) {
168 self.$rulesList.append(
168 self.$rulesList.append(
169 self.addRule(
169 self.addRule(
170 _gettext('Reviewers picked from source code changes.'))
170 _gettext('Reviewers picked from source code changes.'))
171 )
171 )
172 }
172 }
173 if (data.rules.forbid_adding_reviewers) {
173 if (data.rules.forbid_adding_reviewers) {
174 $('#add_reviewer_input').remove();
174 $('#add_reviewer_input').remove();
175 self.$rulesList.append(
175 self.$rulesList.append(
176 self.addRule(
176 self.addRule(
177 _gettext('Adding new reviewers is forbidden.'))
177 _gettext('Adding new reviewers is forbidden.'))
178 )
178 )
179 }
179 }
180 if (data.rules.forbid_author_to_review) {
180 if (data.rules.forbid_author_to_review) {
181 self.forbidReviewUsers.push(data.rules_data.pr_author);
181 self.forbidReviewUsers.push(data.rules_data.pr_author);
182 self.$rulesList.append(
182 self.$rulesList.append(
183 self.addRule(
183 self.addRule(
184 _gettext('Author is not allowed to be a reviewer.'))
184 _gettext('Author is not allowed to be a reviewer.'))
185 )
185 )
186 }
186 }
187 if (data.rules.forbid_commit_author_to_review) {
187 if (data.rules.forbid_commit_author_to_review) {
188
188
189 if (data.rules_data.forbidden_users) {
189 if (data.rules_data.forbidden_users) {
190 $.each(data.rules_data.forbidden_users, function(index, member_data) {
190 $.each(data.rules_data.forbidden_users, function(index, member_data) {
191 self.forbidReviewUsers.push(member_data)
191 self.forbidReviewUsers.push(member_data)
192 });
192 });
193
193
194 }
194 }
195
195
196 self.$rulesList.append(
196 self.$rulesList.append(
197 self.addRule(
197 self.addRule(
198 _gettext('Commit Authors are not allowed to be a reviewer.'))
198 _gettext('Commit Authors are not allowed to be a reviewer.'))
199 )
199 )
200 }
200 }
201
201
202 return self.forbidReviewUsers
202 return self.forbidReviewUsers
203 };
203 };
204
204
205 this.loadDefaultReviewers = function(sourceRepo, sourceRef, targetRepo, targetRef) {
205 this.loadDefaultReviewers = function(sourceRepo, sourceRef, targetRepo, targetRef) {
206
206
207 if (self.currentRequest) {
207 if (self.currentRequest) {
208 // make sure we cleanup old running requests before triggering this
208 // make sure we cleanup old running requests before triggering this
209 // again
209 // again
210 self.currentRequest.abort();
210 self.currentRequest.abort();
211 }
211 }
212
212
213 $('.calculate-reviewers').show();
213 $('.calculate-reviewers').show();
214 // reset reviewer members
214 // reset reviewer members
215 self.$reviewMembers.empty();
215 self.$reviewMembers.empty();
216
216
217 prButtonLock(true, null, 'reviewers');
217 prButtonLock(true, null, 'reviewers');
218 $('#user').hide(); // hide user autocomplete before load
218 $('#user').hide(); // hide user autocomplete before load
219
219
220 if (sourceRef.length !== 3 || targetRef.length !== 3) {
220 if (sourceRef.length !== 3 || targetRef.length !== 3) {
221 // don't load defaults in case we're missing some refs...
221 // don't load defaults in case we're missing some refs...
222 $('.calculate-reviewers').hide();
222 $('.calculate-reviewers').hide();
223 return
223 return
224 }
224 }
225
225
226 var url = pyroutes.url('repo_default_reviewers_data',
226 var url = pyroutes.url('repo_default_reviewers_data',
227 {
227 {
228 'repo_name': templateContext.repo_name,
228 'repo_name': templateContext.repo_name,
229 'source_repo': sourceRepo,
229 'source_repo': sourceRepo,
230 'source_ref': sourceRef[2],
230 'source_ref': sourceRef[2],
231 'target_repo': targetRepo,
231 'target_repo': targetRepo,
232 'target_ref': targetRef[2]
232 'target_ref': targetRef[2]
233 });
233 });
234
234
235 self.currentRequest = $.get(url)
235 self.currentRequest = $.get(url)
236 .done(function(data) {
236 .done(function(data) {
237 self.currentRequest = null;
237 self.currentRequest = null;
238
238
239 // review rules
239 // review rules
240 self.loadReviewRules(data);
240 self.loadReviewRules(data);
241
241
242 for (var i = 0; i < data.reviewers.length; i++) {
242 for (var i = 0; i < data.reviewers.length; i++) {
243 var reviewer = data.reviewers[i];
243 var reviewer = data.reviewers[i];
244 self.addReviewMember(
244 self.addReviewMember(
245 reviewer, reviewer.reasons, reviewer.mandatory);
245 reviewer, reviewer.reasons, reviewer.mandatory);
246 }
246 }
247 $('.calculate-reviewers').hide();
247 $('.calculate-reviewers').hide();
248 prButtonLock(false, null, 'reviewers');
248 prButtonLock(false, null, 'reviewers');
249 $('#user').show(); // show user autocomplete after load
249 $('#user').show(); // show user autocomplete after load
250 });
250 });
251 };
251 };
252
252
253 // check those, refactor
253 // check those, refactor
254 this.removeReviewMember = function(reviewer_id, mark_delete) {
254 this.removeReviewMember = function(reviewer_id, mark_delete) {
255 var reviewer = $('#reviewer_{0}'.format(reviewer_id));
255 var reviewer = $('#reviewer_{0}'.format(reviewer_id));
256
256
257 if(typeof(mark_delete) === undefined){
257 if(typeof(mark_delete) === undefined){
258 mark_delete = false;
258 mark_delete = false;
259 }
259 }
260
260
261 if(mark_delete === true){
261 if(mark_delete === true){
262 if (reviewer){
262 if (reviewer){
263 // now delete the input
263 // now delete the input
264 $('#reviewer_{0} input'.format(reviewer_id)).remove();
264 $('#reviewer_{0} input'.format(reviewer_id)).remove();
265 // mark as to-delete
265 // mark as to-delete
266 var obj = $('#reviewer_{0}_name'.format(reviewer_id));
266 var obj = $('#reviewer_{0}_name'.format(reviewer_id));
267 obj.addClass('to-delete');
267 obj.addClass('to-delete');
268 obj.css({"text-decoration":"line-through", "opacity": 0.5});
268 obj.css({"text-decoration":"line-through", "opacity": 0.5});
269 }
269 }
270 }
270 }
271 else{
271 else{
272 $('#reviewer_{0}'.format(reviewer_id)).remove();
272 $('#reviewer_{0}'.format(reviewer_id)).remove();
273 }
273 }
274 };
274 };
275 this.reviewMemberEntry = function() {
275 this.reviewMemberEntry = function() {
276
276
277 };
277 };
278 this.addReviewMember = function(reviewer_obj, reasons, mandatory) {
278 this.addReviewMember = function(reviewer_obj, reasons, mandatory) {
279 var members = self.$reviewMembers.get(0);
279 var members = self.$reviewMembers.get(0);
280 var id = reviewer_obj.user_id;
280 var id = reviewer_obj.user_id;
281 var username = reviewer_obj.username;
281 var username = reviewer_obj.username;
282
282
283 var reasons = reasons || [];
283 var reasons = reasons || [];
284 var mandatory = mandatory || false;
284 var mandatory = mandatory || false;
285
285
286 // register IDS to check if we don't have this ID already in
286 // register IDS to check if we don't have this ID already in
287 var currentIds = [];
287 var currentIds = [];
288 var _els = self.$reviewMembers.find('li').toArray();
288 var _els = self.$reviewMembers.find('li').toArray();
289 for (el in _els){
289 for (el in _els){
290 currentIds.push(_els[el].id)
290 currentIds.push(_els[el].id)
291 }
291 }
292
292
293 var userAllowedReview = function(userId) {
293 var userAllowedReview = function(userId) {
294 var allowed = true;
294 var allowed = true;
295 $.each(self.forbidReviewUsers, function(index, member_data) {
295 $.each(self.forbidReviewUsers, function(index, member_data) {
296 if (parseInt(userId) === member_data['user_id']) {
296 if (parseInt(userId) === member_data['user_id']) {
297 allowed = false;
297 allowed = false;
298 return false // breaks the loop
298 return false // breaks the loop
299 }
299 }
300 });
300 });
301 return allowed
301 return allowed
302 };
302 };
303
303
304 var userAllowed = userAllowedReview(id);
304 var userAllowed = userAllowedReview(id);
305 if (!userAllowed){
305 if (!userAllowed){
306 alert(_gettext('User `{0}` not allowed to be a reviewer').format(username));
306 alert(_gettext('User `{0}` not allowed to be a reviewer').format(username));
307 } else {
307 } else {
308 // only add if it's not there
308 // only add if it's not there
309 var alreadyReviewer = currentIds.indexOf('reviewer_'+id) != -1;
309 var alreadyReviewer = currentIds.indexOf('reviewer_'+id) != -1;
310
310
311 if (alreadyReviewer) {
311 if (alreadyReviewer) {
312 alert(_gettext('User `{0}` already in reviewers').format(username));
312 alert(_gettext('User `{0}` already in reviewers').format(username));
313 } else {
313 } else {
314 members.innerHTML += renderTemplate('reviewMemberEntry', {
314 members.innerHTML += renderTemplate('reviewMemberEntry', {
315 'member': reviewer_obj,
315 'member': reviewer_obj,
316 'mandatory': mandatory,
316 'mandatory': mandatory,
317 'allowed_to_update': true,
317 'allowed_to_update': true,
318 'review_status': 'not_reviewed',
318 'review_status': 'not_reviewed',
319 'review_status_label': _gettext('Not Reviewed'),
319 'review_status_label': _gettext('Not Reviewed'),
320 'reasons': reasons,
320 'reasons': reasons,
321 'create': true
321 'create': true
322 });
322 });
323 }
323 }
324 }
324 }
325
325
326 };
326 };
327
327
328 this.updateReviewers = function(repo_name, pull_request_id){
328 this.updateReviewers = function(repo_name, pull_request_id){
329 var postData = $('#reviewers input').serialize();
329 var postData = $('#reviewers input').serialize();
330 _updatePullRequest(repo_name, pull_request_id, postData);
330 _updatePullRequest(repo_name, pull_request_id, postData);
331 };
331 };
332
332
333 };
333 };
334
334
335
335
336 var _updatePullRequest = function(repo_name, pull_request_id, postData) {
336 var _updatePullRequest = function(repo_name, pull_request_id, postData) {
337 var url = pyroutes.url(
337 var url = pyroutes.url(
338 'pullrequest_update',
338 'pullrequest_update',
339 {"repo_name": repo_name, "pull_request_id": pull_request_id});
339 {"repo_name": repo_name, "pull_request_id": pull_request_id});
340 if (typeof postData === 'string' ) {
340 if (typeof postData === 'string' ) {
341 postData += '&csrf_token=' + CSRF_TOKEN;
341 postData += '&csrf_token=' + CSRF_TOKEN;
342 } else {
342 } else {
343 postData.csrf_token = CSRF_TOKEN;
343 postData.csrf_token = CSRF_TOKEN;
344 }
344 }
345 var success = function(o) {
345 var success = function(o) {
346 window.location.reload();
346 window.location.reload();
347 };
347 };
348 ajaxPOST(url, postData, success);
348 ajaxPOST(url, postData, success);
349 };
349 };
350
350
351 /**
351 /**
352 * PULL REQUEST update commits
352 * PULL REQUEST update commits
353 */
353 */
354 var updateCommits = function(repo_name, pull_request_id) {
354 var updateCommits = function(repo_name, pull_request_id) {
355 var postData = {
355 var postData = {
356 'update_commits': true};
356 'update_commits': true};
357 _updatePullRequest(repo_name, pull_request_id, postData);
357 _updatePullRequest(repo_name, pull_request_id, postData);
358 };
358 };
359
359
360
360
361 /**
361 /**
362 * PULL REQUEST edit info
362 * PULL REQUEST edit info
363 */
363 */
364 var editPullRequest = function(repo_name, pull_request_id, title, description) {
364 var editPullRequest = function(repo_name, pull_request_id, title, description) {
365 var url = pyroutes.url(
365 var url = pyroutes.url(
366 'pullrequest_update',
366 'pullrequest_update',
367 {"repo_name": repo_name, "pull_request_id": pull_request_id});
367 {"repo_name": repo_name, "pull_request_id": pull_request_id});
368
368
369 var postData = {
369 var postData = {
370 'title': title,
370 'title': title,
371 'description': description,
371 'description': description,
372 'edit_pull_request': true,
372 'edit_pull_request': true,
373 'csrf_token': CSRF_TOKEN
373 'csrf_token': CSRF_TOKEN
374 };
374 };
375 var success = function(o) {
375 var success = function(o) {
376 window.location.reload();
376 window.location.reload();
377 };
377 };
378 ajaxPOST(url, postData, success);
378 ajaxPOST(url, postData, success);
379 };
379 };
380
380
381 var initPullRequestsCodeMirror = function (textAreaId) {
382 var ta = $(textAreaId).get(0);
383 var initialHeight = '100px';
384
385 // default options
386 var codeMirrorOptions = {
387 mode: "text",
388 lineNumbers: false,
389 indentUnit: 4,
390 theme: 'rc-input'
391 };
392
393 var codeMirrorInstance = CodeMirror.fromTextArea(ta, codeMirrorOptions);
394 // marker for manually set description
395 codeMirrorInstance._userDefinedDesc = false;
396 codeMirrorInstance.setSize(null, initialHeight);
397 codeMirrorInstance.on("change", function(instance, changeObj) {
398 var height = initialHeight;
399 var lines = instance.lineCount();
400 if (lines > 6 && lines < 20) {
401 height = "auto"
402 }
403 else if (lines >= 20) {
404 height = 20 * 15;
405 }
406 instance.setSize(null, height);
407
408 // detect if the change was trigger by auto desc, or user input
409 changeOrigin = changeObj.origin;
410
411 if (changeOrigin === "setValue") {
412 cmLog.debug('Change triggered by setValue');
413 }
414 else {
415 cmLog.debug('user triggered change !');
416 // set special marker to indicate user has created an input.
417 instance._userDefinedDesc = true;
418 }
419
420 });
421
422 return codeMirrorInstance
423 };
424
381
425 /**
382 /**
426 * Reviewer autocomplete
383 * Reviewer autocomplete
427 */
384 */
428 var ReviewerAutoComplete = function(inputId) {
385 var ReviewerAutoComplete = function(inputId) {
429 $(inputId).autocomplete({
386 $(inputId).autocomplete({
430 serviceUrl: pyroutes.url('user_autocomplete_data'),
387 serviceUrl: pyroutes.url('user_autocomplete_data'),
431 minChars:2,
388 minChars:2,
432 maxHeight:400,
389 maxHeight:400,
433 deferRequestBy: 300, //miliseconds
390 deferRequestBy: 300, //miliseconds
434 showNoSuggestionNotice: true,
391 showNoSuggestionNotice: true,
435 tabDisabled: true,
392 tabDisabled: true,
436 autoSelectFirst: true,
393 autoSelectFirst: true,
437 params: { user_id: templateContext.rhodecode_user.user_id, user_groups:true, user_groups_expand:true, skip_default_user:true },
394 params: { user_id: templateContext.rhodecode_user.user_id, user_groups:true, user_groups_expand:true, skip_default_user:true },
438 formatResult: autocompleteFormatResult,
395 formatResult: autocompleteFormatResult,
439 lookupFilter: autocompleteFilterResult,
396 lookupFilter: autocompleteFilterResult,
440 onSelect: function(element, data) {
397 onSelect: function(element, data) {
441 var mandatory = false;
398 var mandatory = false;
442 var reasons = [_gettext('added manually by "{0}"').format(templateContext.rhodecode_user.username)];
399 var reasons = [_gettext('added manually by "{0}"').format(templateContext.rhodecode_user.username)];
443
400
444 // add whole user groups
401 // add whole user groups
445 if (data.value_type == 'user_group') {
402 if (data.value_type == 'user_group') {
446 reasons.push(_gettext('member of "{0}"').format(data.value_display));
403 reasons.push(_gettext('member of "{0}"').format(data.value_display));
447
404
448 $.each(data.members, function(index, member_data) {
405 $.each(data.members, function(index, member_data) {
449 var reviewer = member_data;
406 var reviewer = member_data;
450 reviewer['user_id'] = member_data['id'];
407 reviewer['user_id'] = member_data['id'];
451 reviewer['gravatar_link'] = member_data['icon_link'];
408 reviewer['gravatar_link'] = member_data['icon_link'];
452 reviewer['user_link'] = member_data['profile_link'];
409 reviewer['user_link'] = member_data['profile_link'];
453 reviewer['rules'] = [];
410 reviewer['rules'] = [];
454 reviewersController.addReviewMember(reviewer, reasons, mandatory);
411 reviewersController.addReviewMember(reviewer, reasons, mandatory);
455 })
412 })
456 }
413 }
457 // add single user
414 // add single user
458 else {
415 else {
459 var reviewer = data;
416 var reviewer = data;
460 reviewer['user_id'] = data['id'];
417 reviewer['user_id'] = data['id'];
461 reviewer['gravatar_link'] = data['icon_link'];
418 reviewer['gravatar_link'] = data['icon_link'];
462 reviewer['user_link'] = data['profile_link'];
419 reviewer['user_link'] = data['profile_link'];
463 reviewer['rules'] = [];
420 reviewer['rules'] = [];
464 reviewersController.addReviewMember(reviewer, reasons, mandatory);
421 reviewersController.addReviewMember(reviewer, reasons, mandatory);
465 }
422 }
466
423
467 $(inputId).val('');
424 $(inputId).val('');
468 }
425 }
469 });
426 });
470 };
427 };
471
428
472
429
473 VersionController = function () {
430 VersionController = function () {
474 var self = this;
431 var self = this;
475 this.$verSource = $('input[name=ver_source]');
432 this.$verSource = $('input[name=ver_source]');
476 this.$verTarget = $('input[name=ver_target]');
433 this.$verTarget = $('input[name=ver_target]');
477 this.$showVersionDiff = $('#show-version-diff');
434 this.$showVersionDiff = $('#show-version-diff');
478
435
479 this.adjustRadioSelectors = function (curNode) {
436 this.adjustRadioSelectors = function (curNode) {
480 var getVal = function (item) {
437 var getVal = function (item) {
481 if (item == 'latest') {
438 if (item == 'latest') {
482 return Number.MAX_SAFE_INTEGER
439 return Number.MAX_SAFE_INTEGER
483 }
440 }
484 else {
441 else {
485 return parseInt(item)
442 return parseInt(item)
486 }
443 }
487 };
444 };
488
445
489 var curVal = getVal($(curNode).val());
446 var curVal = getVal($(curNode).val());
490 var cleared = false;
447 var cleared = false;
491
448
492 $.each(self.$verSource, function (index, value) {
449 $.each(self.$verSource, function (index, value) {
493 var elVal = getVal($(value).val());
450 var elVal = getVal($(value).val());
494
451
495 if (elVal > curVal) {
452 if (elVal > curVal) {
496 if ($(value).is(':checked')) {
453 if ($(value).is(':checked')) {
497 cleared = true;
454 cleared = true;
498 }
455 }
499 $(value).attr('disabled', 'disabled');
456 $(value).attr('disabled', 'disabled');
500 $(value).removeAttr('checked');
457 $(value).removeAttr('checked');
501 $(value).css({'opacity': 0.1});
458 $(value).css({'opacity': 0.1});
502 }
459 }
503 else {
460 else {
504 $(value).css({'opacity': 1});
461 $(value).css({'opacity': 1});
505 $(value).removeAttr('disabled');
462 $(value).removeAttr('disabled');
506 }
463 }
507 });
464 });
508
465
509 if (cleared) {
466 if (cleared) {
510 // if we unchecked an active, set the next one to same loc.
467 // if we unchecked an active, set the next one to same loc.
511 $(this.$verSource).filter('[value={0}]'.format(
468 $(this.$verSource).filter('[value={0}]'.format(
512 curVal)).attr('checked', 'checked');
469 curVal)).attr('checked', 'checked');
513 }
470 }
514
471
515 self.setLockAction(false,
472 self.setLockAction(false,
516 $(curNode).data('verPos'),
473 $(curNode).data('verPos'),
517 $(this.$verSource).filter(':checked').data('verPos')
474 $(this.$verSource).filter(':checked').data('verPos')
518 );
475 );
519 };
476 };
520
477
521
478
522 this.attachVersionListener = function () {
479 this.attachVersionListener = function () {
523 self.$verTarget.change(function (e) {
480 self.$verTarget.change(function (e) {
524 self.adjustRadioSelectors(this)
481 self.adjustRadioSelectors(this)
525 });
482 });
526 self.$verSource.change(function (e) {
483 self.$verSource.change(function (e) {
527 self.adjustRadioSelectors(self.$verTarget.filter(':checked'))
484 self.adjustRadioSelectors(self.$verTarget.filter(':checked'))
528 });
485 });
529 };
486 };
530
487
531 this.init = function () {
488 this.init = function () {
532
489
533 var curNode = self.$verTarget.filter(':checked');
490 var curNode = self.$verTarget.filter(':checked');
534 self.adjustRadioSelectors(curNode);
491 self.adjustRadioSelectors(curNode);
535 self.setLockAction(true);
492 self.setLockAction(true);
536 self.attachVersionListener();
493 self.attachVersionListener();
537
494
538 };
495 };
539
496
540 this.setLockAction = function (state, selectedVersion, otherVersion) {
497 this.setLockAction = function (state, selectedVersion, otherVersion) {
541 var $showVersionDiff = this.$showVersionDiff;
498 var $showVersionDiff = this.$showVersionDiff;
542
499
543 if (state) {
500 if (state) {
544 $showVersionDiff.attr('disabled', 'disabled');
501 $showVersionDiff.attr('disabled', 'disabled');
545 $showVersionDiff.addClass('disabled');
502 $showVersionDiff.addClass('disabled');
546 $showVersionDiff.html($showVersionDiff.data('labelTextLocked'));
503 $showVersionDiff.html($showVersionDiff.data('labelTextLocked'));
547 }
504 }
548 else {
505 else {
549 $showVersionDiff.removeAttr('disabled');
506 $showVersionDiff.removeAttr('disabled');
550 $showVersionDiff.removeClass('disabled');
507 $showVersionDiff.removeClass('disabled');
551
508
552 if (selectedVersion == otherVersion) {
509 if (selectedVersion == otherVersion) {
553 $showVersionDiff.html($showVersionDiff.data('labelTextShow'));
510 $showVersionDiff.html($showVersionDiff.data('labelTextShow'));
554 } else {
511 } else {
555 $showVersionDiff.html($showVersionDiff.data('labelTextDiff'));
512 $showVersionDiff.html($showVersionDiff.data('labelTextDiff'));
556 }
513 }
557 }
514 }
558
515
559 };
516 };
560
517
561 this.showVersionDiff = function () {
518 this.showVersionDiff = function () {
562 var target = self.$verTarget.filter(':checked');
519 var target = self.$verTarget.filter(':checked');
563 var source = self.$verSource.filter(':checked');
520 var source = self.$verSource.filter(':checked');
564
521
565 if (target.val() && source.val()) {
522 if (target.val() && source.val()) {
566 var params = {
523 var params = {
567 'pull_request_id': templateContext.pull_request_data.pull_request_id,
524 'pull_request_id': templateContext.pull_request_data.pull_request_id,
568 'repo_name': templateContext.repo_name,
525 'repo_name': templateContext.repo_name,
569 'version': target.val(),
526 'version': target.val(),
570 'from_version': source.val()
527 'from_version': source.val()
571 };
528 };
572 window.location = pyroutes.url('pullrequest_show', params)
529 window.location = pyroutes.url('pullrequest_show', params)
573 }
530 }
574
531
575 return false;
532 return false;
576 };
533 };
577
534
578 this.toggleVersionView = function (elem) {
535 this.toggleVersionView = function (elem) {
579
536
580 if (this.$showVersionDiff.is(':visible')) {
537 if (this.$showVersionDiff.is(':visible')) {
581 $('.version-pr').hide();
538 $('.version-pr').hide();
582 this.$showVersionDiff.hide();
539 this.$showVersionDiff.hide();
583 $(elem).html($(elem).data('toggleOn'))
540 $(elem).html($(elem).data('toggleOn'))
584 } else {
541 } else {
585 $('.version-pr').show();
542 $('.version-pr').show();
586 this.$showVersionDiff.show();
543 this.$showVersionDiff.show();
587 $(elem).html($(elem).data('toggleOff'))
544 $(elem).html($(elem).data('toggleOff'))
588 }
545 }
589
546
590 return false
547 return false
591 }
548 }
592
549
593 }; No newline at end of file
550 };
@@ -1,377 +1,425 b''
1 ## DATA TABLE RE USABLE ELEMENTS
1 ## DATA TABLE RE USABLE ELEMENTS
2 ## usage:
2 ## usage:
3 ## <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
3 ## <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
4 <%namespace name="base" file="/base/base.mako"/>
4 <%namespace name="base" file="/base/base.mako"/>
5
5
6 <%def name="metatags_help()">
6 <%def name="metatags_help()">
7 <table>
7 <table>
8 <%
8 <%
9 example_tags = [
9 example_tags = [
10 ('state','[stable]'),
10 ('state','[stable]'),
11 ('state','[stale]'),
11 ('state','[stale]'),
12 ('state','[featured]'),
12 ('state','[featured]'),
13 ('state','[dev]'),
13 ('state','[dev]'),
14 ('state','[dead]'),
14 ('state','[dead]'),
15 ('state','[deprecated]'),
15 ('state','[deprecated]'),
16
16
17 ('label','[personal]'),
17 ('label','[personal]'),
18 ('generic','[v2.0.0]'),
18 ('generic','[v2.0.0]'),
19
19
20 ('lang','[lang =&gt; JavaScript]'),
20 ('lang','[lang =&gt; JavaScript]'),
21 ('license','[license =&gt; LicenseName]'),
21 ('license','[license =&gt; LicenseName]'),
22
22
23 ('ref','[requires =&gt; RepoName]'),
23 ('ref','[requires =&gt; RepoName]'),
24 ('ref','[recommends =&gt; GroupName]'),
24 ('ref','[recommends =&gt; GroupName]'),
25 ('ref','[conflicts =&gt; SomeName]'),
25 ('ref','[conflicts =&gt; SomeName]'),
26 ('ref','[base =&gt; SomeName]'),
26 ('ref','[base =&gt; SomeName]'),
27 ('url','[url =&gt; [linkName](https://rhodecode.com)]'),
27 ('url','[url =&gt; [linkName](https://rhodecode.com)]'),
28 ('see','[see =&gt; http://rhodecode.com]'),
28 ('see','[see =&gt; http://rhodecode.com]'),
29 ]
29 ]
30 %>
30 %>
31 % for tag_type, tag in example_tags:
31 % for tag_type, tag in example_tags:
32 <tr>
32 <tr>
33 <td>${tag|n}</td>
33 <td>${tag|n}</td>
34 <td>${h.style_metatag(tag_type, tag)|n}</td>
34 <td>${h.style_metatag(tag_type, tag)|n}</td>
35 </tr>
35 </tr>
36 % endfor
36 % endfor
37 </table>
37 </table>
38 </%def>
38 </%def>
39
39
40 ## REPOSITORY RENDERERS
40 ## REPOSITORY RENDERERS
41 <%def name="quick_menu(repo_name)">
41 <%def name="quick_menu(repo_name)">
42 <i class="icon-more"></i>
42 <i class="icon-more"></i>
43 <div class="menu_items_container hidden">
43 <div class="menu_items_container hidden">
44 <ul class="menu_items">
44 <ul class="menu_items">
45 <li>
45 <li>
46 <a title="${_('Summary')}" href="${h.route_path('repo_summary',repo_name=repo_name)}">
46 <a title="${_('Summary')}" href="${h.route_path('repo_summary',repo_name=repo_name)}">
47 <span>${_('Summary')}</span>
47 <span>${_('Summary')}</span>
48 </a>
48 </a>
49 </li>
49 </li>
50 <li>
50 <li>
51 <a title="${_('Changelog')}" href="${h.route_path('repo_changelog',repo_name=repo_name)}">
51 <a title="${_('Changelog')}" href="${h.route_path('repo_changelog',repo_name=repo_name)}">
52 <span>${_('Changelog')}</span>
52 <span>${_('Changelog')}</span>
53 </a>
53 </a>
54 </li>
54 </li>
55 <li>
55 <li>
56 <a title="${_('Files')}" href="${h.route_path('repo_files:default_commit',repo_name=repo_name)}">
56 <a title="${_('Files')}" href="${h.route_path('repo_files:default_commit',repo_name=repo_name)}">
57 <span>${_('Files')}</span>
57 <span>${_('Files')}</span>
58 </a>
58 </a>
59 </li>
59 </li>
60 <li>
60 <li>
61 <a title="${_('Fork')}" href="${h.route_path('repo_fork_new',repo_name=repo_name)}">
61 <a title="${_('Fork')}" href="${h.route_path('repo_fork_new',repo_name=repo_name)}">
62 <span>${_('Fork')}</span>
62 <span>${_('Fork')}</span>
63 </a>
63 </a>
64 </li>
64 </li>
65 </ul>
65 </ul>
66 </div>
66 </div>
67 </%def>
67 </%def>
68
68
69 <%def name="repo_name(name,rtype,rstate,private,fork_of,short_name=False,admin=False)">
69 <%def name="repo_name(name,rtype,rstate,private,fork_of,short_name=False,admin=False)">
70 <%
70 <%
71 def get_name(name,short_name=short_name):
71 def get_name(name,short_name=short_name):
72 if short_name:
72 if short_name:
73 return name.split('/')[-1]
73 return name.split('/')[-1]
74 else:
74 else:
75 return name
75 return name
76 %>
76 %>
77 <div class="${'repo_state_pending' if rstate == 'repo_state_pending' else ''} truncate">
77 <div class="${'repo_state_pending' if rstate == 'repo_state_pending' else ''} truncate">
78 ##NAME
78 ##NAME
79 <a href="${h.route_path('edit_repo',repo_name=name) if admin else h.route_path('repo_summary',repo_name=name)}">
79 <a href="${h.route_path('edit_repo',repo_name=name) if admin else h.route_path('repo_summary',repo_name=name)}">
80
80
81 ##TYPE OF REPO
81 ##TYPE OF REPO
82 %if h.is_hg(rtype):
82 %if h.is_hg(rtype):
83 <span title="${_('Mercurial repository')}"><i class="icon-hg" style="font-size: 14px;"></i></span>
83 <span title="${_('Mercurial repository')}"><i class="icon-hg" style="font-size: 14px;"></i></span>
84 %elif h.is_git(rtype):
84 %elif h.is_git(rtype):
85 <span title="${_('Git repository')}"><i class="icon-git" style="font-size: 14px"></i></span>
85 <span title="${_('Git repository')}"><i class="icon-git" style="font-size: 14px"></i></span>
86 %elif h.is_svn(rtype):
86 %elif h.is_svn(rtype):
87 <span title="${_('Subversion repository')}"><i class="icon-svn" style="font-size: 14px"></i></span>
87 <span title="${_('Subversion repository')}"><i class="icon-svn" style="font-size: 14px"></i></span>
88 %endif
88 %endif
89
89
90 ##PRIVATE/PUBLIC
90 ##PRIVATE/PUBLIC
91 %if private and c.visual.show_private_icon:
91 %if private and c.visual.show_private_icon:
92 <i class="icon-lock" title="${_('Private repository')}"></i>
92 <i class="icon-lock" title="${_('Private repository')}"></i>
93 %elif not private and c.visual.show_public_icon:
93 %elif not private and c.visual.show_public_icon:
94 <i class="icon-unlock-alt" title="${_('Public repository')}"></i>
94 <i class="icon-unlock-alt" title="${_('Public repository')}"></i>
95 %else:
95 %else:
96 <span></span>
96 <span></span>
97 %endif
97 %endif
98 ${get_name(name)}
98 ${get_name(name)}
99 </a>
99 </a>
100 %if fork_of:
100 %if fork_of:
101 <a href="${h.route_path('repo_summary',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a>
101 <a href="${h.route_path('repo_summary',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a>
102 %endif
102 %endif
103 %if rstate == 'repo_state_pending':
103 %if rstate == 'repo_state_pending':
104 <span class="creation_in_progress tooltip" title="${_('This repository is being created in a background task')}">
104 <span class="creation_in_progress tooltip" title="${_('This repository is being created in a background task')}">
105 (${_('creating...')})
105 (${_('creating...')})
106 </span>
106 </span>
107 %endif
107 %endif
108 </div>
108 </div>
109 </%def>
109 </%def>
110
110
111 <%def name="repo_desc(description, stylify_metatags)">
111 <%def name="repo_desc(description, stylify_metatags)">
112 <%
112 <%
113 tags, description = h.extract_metatags(description)
113 tags, description = h.extract_metatags(description)
114 %>
114 %>
115
115
116 <div class="truncate-wrap">
116 <div class="truncate-wrap">
117 % if stylify_metatags:
117 % if stylify_metatags:
118 % for tag_type, tag in tags:
118 % for tag_type, tag in tags:
119 ${h.style_metatag(tag_type, tag)|n}
119 ${h.style_metatag(tag_type, tag)|n}
120 % endfor
120 % endfor
121 % endif
121 % endif
122 ${description}
122 ${description}
123 </div>
123 </div>
124
124
125 </%def>
125 </%def>
126
126
127 <%def name="last_change(last_change)">
127 <%def name="last_change(last_change)">
128 ${h.age_component(last_change, time_is_local=True)}
128 ${h.age_component(last_change, time_is_local=True)}
129 </%def>
129 </%def>
130
130
131 <%def name="revision(name,rev,tip,author,last_msg, commit_date)">
131 <%def name="revision(name,rev,tip,author,last_msg, commit_date)">
132 <div>
132 <div>
133 %if rev >= 0:
133 %if rev >= 0:
134 <code><a title="${h.tooltip('%s\n%s\n\n%s' % (author, commit_date, last_msg))}" class="tooltip" href="${h.route_path('repo_commit',repo_name=name,commit_id=tip)}">${'r%s:%s' % (rev,h.short_id(tip))}</a></code>
134 <code><a title="${h.tooltip('%s\n%s\n\n%s' % (author, commit_date, last_msg))}" class="tooltip" href="${h.route_path('repo_commit',repo_name=name,commit_id=tip)}">${'r%s:%s' % (rev,h.short_id(tip))}</a></code>
135 %else:
135 %else:
136 ${_('No commits yet')}
136 ${_('No commits yet')}
137 %endif
137 %endif
138 </div>
138 </div>
139 </%def>
139 </%def>
140
140
141 <%def name="rss(name)">
141 <%def name="rss(name)">
142 %if c.rhodecode_user.username != h.DEFAULT_USER:
142 %if c.rhodecode_user.username != h.DEFAULT_USER:
143 <a title="${h.tooltip(_('Subscribe to %s rss feed')% name)}" href="${h.route_path('rss_feed_home', repo_name=name, _query=dict(auth_token=c.rhodecode_user.feed_token))}"><i class="icon-rss-sign"></i></a>
143 <a title="${h.tooltip(_('Subscribe to %s rss feed')% name)}" href="${h.route_path('rss_feed_home', repo_name=name, _query=dict(auth_token=c.rhodecode_user.feed_token))}"><i class="icon-rss-sign"></i></a>
144 %else:
144 %else:
145 <a title="${h.tooltip(_('Subscribe to %s rss feed')% name)}" href="${h.route_path('rss_feed_home', repo_name=name)}"><i class="icon-rss-sign"></i></a>
145 <a title="${h.tooltip(_('Subscribe to %s rss feed')% name)}" href="${h.route_path('rss_feed_home', repo_name=name)}"><i class="icon-rss-sign"></i></a>
146 %endif
146 %endif
147 </%def>
147 </%def>
148
148
149 <%def name="atom(name)">
149 <%def name="atom(name)">
150 %if c.rhodecode_user.username != h.DEFAULT_USER:
150 %if c.rhodecode_user.username != h.DEFAULT_USER:
151 <a title="${h.tooltip(_('Subscribe to %s atom feed')% name)}" href="${h.route_path('atom_feed_home', repo_name=name, _query=dict(auth_token=c.rhodecode_user.feed_token))}"><i class="icon-rss-sign"></i></a>
151 <a title="${h.tooltip(_('Subscribe to %s atom feed')% name)}" href="${h.route_path('atom_feed_home', repo_name=name, _query=dict(auth_token=c.rhodecode_user.feed_token))}"><i class="icon-rss-sign"></i></a>
152 %else:
152 %else:
153 <a title="${h.tooltip(_('Subscribe to %s atom feed')% name)}" href="${h.route_path('atom_feed_home', repo_name=name)}"><i class="icon-rss-sign"></i></a>
153 <a title="${h.tooltip(_('Subscribe to %s atom feed')% name)}" href="${h.route_path('atom_feed_home', repo_name=name)}"><i class="icon-rss-sign"></i></a>
154 %endif
154 %endif
155 </%def>
155 </%def>
156
156
157 <%def name="user_gravatar(email, size=16)">
157 <%def name="user_gravatar(email, size=16)">
158 <div class="rc-user tooltip" title="${h.tooltip(h.author_string(email))}">
158 <div class="rc-user tooltip" title="${h.tooltip(h.author_string(email))}">
159 ${base.gravatar(email, 16)}
159 ${base.gravatar(email, 16)}
160 </div>
160 </div>
161 </%def>
161 </%def>
162
162
163 <%def name="repo_actions(repo_name, super_user=True)">
163 <%def name="repo_actions(repo_name, super_user=True)">
164 <div>
164 <div>
165 <div class="grid_edit">
165 <div class="grid_edit">
166 <a href="${h.route_path('edit_repo',repo_name=repo_name)}" title="${_('Edit')}">
166 <a href="${h.route_path('edit_repo',repo_name=repo_name)}" title="${_('Edit')}">
167 <i class="icon-pencil"></i>Edit</a>
167 <i class="icon-pencil"></i>Edit</a>
168 </div>
168 </div>
169 <div class="grid_delete">
169 <div class="grid_delete">
170 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=repo_name), request=request)}
170 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=repo_name), request=request)}
171 ${h.submit('remove_%s' % repo_name,_('Delete'),class_="btn btn-link btn-danger",
171 ${h.submit('remove_%s' % repo_name,_('Delete'),class_="btn btn-link btn-danger",
172 onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
172 onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
173 ${h.end_form()}
173 ${h.end_form()}
174 </div>
174 </div>
175 </div>
175 </div>
176 </%def>
176 </%def>
177
177
178 <%def name="repo_state(repo_state)">
178 <%def name="repo_state(repo_state)">
179 <div>
179 <div>
180 %if repo_state == 'repo_state_pending':
180 %if repo_state == 'repo_state_pending':
181 <div class="tag tag4">${_('Creating')}</div>
181 <div class="tag tag4">${_('Creating')}</div>
182 %elif repo_state == 'repo_state_created':
182 %elif repo_state == 'repo_state_created':
183 <div class="tag tag1">${_('Created')}</div>
183 <div class="tag tag1">${_('Created')}</div>
184 %else:
184 %else:
185 <div class="tag alert2" title="${h.tooltip(repo_state)}">invalid</div>
185 <div class="tag alert2" title="${h.tooltip(repo_state)}">invalid</div>
186 %endif
186 %endif
187 </div>
187 </div>
188 </%def>
188 </%def>
189
189
190
190
191 ## REPO GROUP RENDERERS
191 ## REPO GROUP RENDERERS
192 <%def name="quick_repo_group_menu(repo_group_name)">
192 <%def name="quick_repo_group_menu(repo_group_name)">
193 <i class="icon-more"></i>
193 <i class="icon-more"></i>
194 <div class="menu_items_container hidden">
194 <div class="menu_items_container hidden">
195 <ul class="menu_items">
195 <ul class="menu_items">
196 <li>
196 <li>
197 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">${_('Summary')}</a>
197 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">${_('Summary')}</a>
198 </li>
198 </li>
199
199
200 </ul>
200 </ul>
201 </div>
201 </div>
202 </%def>
202 </%def>
203
203
204 <%def name="repo_group_name(repo_group_name, children_groups=None)">
204 <%def name="repo_group_name(repo_group_name, children_groups=None)">
205 <div>
205 <div>
206 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">
206 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">
207 <i class="icon-folder-close" title="${_('Repository group')}" style="font-size: 16px"></i>
207 <i class="icon-folder-close" title="${_('Repository group')}" style="font-size: 16px"></i>
208 %if children_groups:
208 %if children_groups:
209 ${h.literal(' &raquo; '.join(children_groups))}
209 ${h.literal(' &raquo; '.join(children_groups))}
210 %else:
210 %else:
211 ${repo_group_name}
211 ${repo_group_name}
212 %endif
212 %endif
213 </a>
213 </a>
214 </div>
214 </div>
215 </%def>
215 </%def>
216
216
217 <%def name="repo_group_desc(description, personal, stylify_metatags)">
217 <%def name="repo_group_desc(description, personal, stylify_metatags)">
218
218
219 <%
219 <%
220 tags, description = h.extract_metatags(description)
220 tags, description = h.extract_metatags(description)
221 %>
221 %>
222
222
223 <div class="truncate-wrap">
223 <div class="truncate-wrap">
224 % if personal:
224 % if personal:
225 <div class="metatag" tag="personal">${_('personal')}</div>
225 <div class="metatag" tag="personal">${_('personal')}</div>
226 % endif
226 % endif
227
227
228 % if stylify_metatags:
228 % if stylify_metatags:
229 % for tag_type, tag in tags:
229 % for tag_type, tag in tags:
230 ${h.style_metatag(tag_type, tag)|n}
230 ${h.style_metatag(tag_type, tag)|n}
231 % endfor
231 % endfor
232 % endif
232 % endif
233 ${description}
233 ${description}
234 </div>
234 </div>
235
235
236 </%def>
236 </%def>
237
237
238 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
238 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
239 <div class="grid_edit">
239 <div class="grid_edit">
240 <a href="${h.route_path('edit_repo_group',repo_group_name=repo_group_name)}" title="${_('Edit')}">Edit</a>
240 <a href="${h.route_path('edit_repo_group',repo_group_name=repo_group_name)}" title="${_('Edit')}">Edit</a>
241 </div>
241 </div>
242 <div class="grid_delete">
242 <div class="grid_delete">
243 ${h.secure_form(h.route_path('edit_repo_group_advanced_delete', repo_group_name=repo_group_name), request=request)}
243 ${h.secure_form(h.route_path('edit_repo_group_advanced_delete', repo_group_name=repo_group_name), request=request)}
244 ${h.submit('remove_%s' % repo_group_name,_('Delete'),class_="btn btn-link btn-danger",
244 ${h.submit('remove_%s' % repo_group_name,_('Delete'),class_="btn btn-link btn-danger",
245 onclick="return confirm('"+_ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)+"');")}
245 onclick="return confirm('"+_ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)+"');")}
246 ${h.end_form()}
246 ${h.end_form()}
247 </div>
247 </div>
248 </%def>
248 </%def>
249
249
250
250
251 <%def name="user_actions(user_id, username)">
251 <%def name="user_actions(user_id, username)">
252 <div class="grid_edit">
252 <div class="grid_edit">
253 <a href="${h.route_path('user_edit',user_id=user_id)}" title="${_('Edit')}">
253 <a href="${h.route_path('user_edit',user_id=user_id)}" title="${_('Edit')}">
254 <i class="icon-pencil"></i>${_('Edit')}</a>
254 <i class="icon-pencil"></i>${_('Edit')}</a>
255 </div>
255 </div>
256 <div class="grid_delete">
256 <div class="grid_delete">
257 ${h.secure_form(h.route_path('user_delete', user_id=user_id), request=request)}
257 ${h.secure_form(h.route_path('user_delete', user_id=user_id), request=request)}
258 ${h.submit('remove_',_('Delete'),id="remove_user_%s" % user_id, class_="btn btn-link btn-danger",
258 ${h.submit('remove_',_('Delete'),id="remove_user_%s" % user_id, class_="btn btn-link btn-danger",
259 onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")}
259 onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")}
260 ${h.end_form()}
260 ${h.end_form()}
261 </div>
261 </div>
262 </%def>
262 </%def>
263
263
264 <%def name="user_group_actions(user_group_id, user_group_name)">
264 <%def name="user_group_actions(user_group_id, user_group_name)">
265 <div class="grid_edit">
265 <div class="grid_edit">
266 <a href="${h.route_path('edit_user_group', user_group_id=user_group_id)}" title="${_('Edit')}">Edit</a>
266 <a href="${h.route_path('edit_user_group', user_group_id=user_group_id)}" title="${_('Edit')}">Edit</a>
267 </div>
267 </div>
268 <div class="grid_delete">
268 <div class="grid_delete">
269 ${h.secure_form(h.route_path('user_groups_delete', user_group_id=user_group_id), request=request)}
269 ${h.secure_form(h.route_path('user_groups_delete', user_group_id=user_group_id), request=request)}
270 ${h.submit('remove_',_('Delete'),id="remove_group_%s" % user_group_id, class_="btn btn-link btn-danger",
270 ${h.submit('remove_',_('Delete'),id="remove_group_%s" % user_group_id, class_="btn btn-link btn-danger",
271 onclick="return confirm('"+_('Confirm to delete this user group: %s') % user_group_name+"');")}
271 onclick="return confirm('"+_('Confirm to delete this user group: %s') % user_group_name+"');")}
272 ${h.end_form()}
272 ${h.end_form()}
273 </div>
273 </div>
274 </%def>
274 </%def>
275
275
276
276
277 <%def name="user_name(user_id, username)">
277 <%def name="user_name(user_id, username)">
278 ${h.link_to(h.person(username, 'username_or_name_or_email'), h.route_path('user_edit', user_id=user_id))}
278 ${h.link_to(h.person(username, 'username_or_name_or_email'), h.route_path('user_edit', user_id=user_id))}
279 </%def>
279 </%def>
280
280
281 <%def name="user_profile(username)">
281 <%def name="user_profile(username)">
282 ${base.gravatar_with_user(username, 16)}
282 ${base.gravatar_with_user(username, 16)}
283 </%def>
283 </%def>
284
284
285 <%def name="user_group_name(user_group_name)">
285 <%def name="user_group_name(user_group_name)">
286 <div>
286 <div>
287 <i class="icon-group" title="${_('User group')}"></i>
287 <i class="icon-group" title="${_('User group')}"></i>
288 ${h.link_to_group(user_group_name)}
288 ${h.link_to_group(user_group_name)}
289 </div>
289 </div>
290 </%def>
290 </%def>
291
291
292
292
293 ## GISTS
293 ## GISTS
294
294
295 <%def name="gist_gravatar(full_contact)">
295 <%def name="gist_gravatar(full_contact)">
296 <div class="gist_gravatar">
296 <div class="gist_gravatar">
297 ${base.gravatar(full_contact, 30)}
297 ${base.gravatar(full_contact, 30)}
298 </div>
298 </div>
299 </%def>
299 </%def>
300
300
301 <%def name="gist_access_id(gist_access_id, full_contact)">
301 <%def name="gist_access_id(gist_access_id, full_contact)">
302 <div>
302 <div>
303 <b>
303 <b>
304 <a href="${h.route_path('gist_show', gist_id=gist_access_id)}">gist: ${gist_access_id}</a>
304 <a href="${h.route_path('gist_show', gist_id=gist_access_id)}">gist: ${gist_access_id}</a>
305 </b>
305 </b>
306 </div>
306 </div>
307 </%def>
307 </%def>
308
308
309 <%def name="gist_author(full_contact, created_on, expires)">
309 <%def name="gist_author(full_contact, created_on, expires)">
310 ${base.gravatar_with_user(full_contact, 16)}
310 ${base.gravatar_with_user(full_contact, 16)}
311 </%def>
311 </%def>
312
312
313
313
314 <%def name="gist_created(created_on)">
314 <%def name="gist_created(created_on)">
315 <div class="created">
315 <div class="created">
316 ${h.age_component(created_on, time_is_local=True)}
316 ${h.age_component(created_on, time_is_local=True)}
317 </div>
317 </div>
318 </%def>
318 </%def>
319
319
320 <%def name="gist_expires(expires)">
320 <%def name="gist_expires(expires)">
321 <div class="created">
321 <div class="created">
322 %if expires == -1:
322 %if expires == -1:
323 ${_('never')}
323 ${_('never')}
324 %else:
324 %else:
325 ${h.age_component(h.time_to_utcdatetime(expires))}
325 ${h.age_component(h.time_to_utcdatetime(expires))}
326 %endif
326 %endif
327 </div>
327 </div>
328 </%def>
328 </%def>
329
329
330 <%def name="gist_type(gist_type)">
330 <%def name="gist_type(gist_type)">
331 %if gist_type != 'public':
331 %if gist_type != 'public':
332 <div class="tag">${_('Private')}</div>
332 <div class="tag">${_('Private')}</div>
333 %endif
333 %endif
334 </%def>
334 </%def>
335
335
336 <%def name="gist_description(gist_description)">
336 <%def name="gist_description(gist_description)">
337 ${gist_description}
337 ${gist_description}
338 </%def>
338 </%def>
339
339
340
340
341 ## PULL REQUESTS GRID RENDERERS
341 ## PULL REQUESTS GRID RENDERERS
342
342
343 <%def name="pullrequest_target_repo(repo_name)">
343 <%def name="pullrequest_target_repo(repo_name)">
344 <div class="truncate">
344 <div class="truncate">
345 ${h.link_to(repo_name,h.route_path('repo_summary',repo_name=repo_name))}
345 ${h.link_to(repo_name,h.route_path('repo_summary',repo_name=repo_name))}
346 </div>
346 </div>
347 </%def>
347 </%def>
348 <%def name="pullrequest_status(status)">
348 <%def name="pullrequest_status(status)">
349 <div class="${'flag_status %s' % status} pull-left"></div>
349 <div class="${'flag_status %s' % status} pull-left"></div>
350 </%def>
350 </%def>
351
351
352 <%def name="pullrequest_title(title, description)">
352 <%def name="pullrequest_title(title, description)">
353 ${title} <br/>
353 ${title}
354 ${h.shorter(description, 40)}
355 </%def>
354 </%def>
356
355
357 <%def name="pullrequest_comments(comments_nr)">
356 <%def name="pullrequest_comments(comments_nr)">
358 <i class="icon-comment"></i> ${comments_nr}
357 <i class="icon-comment"></i> ${comments_nr}
359 </%def>
358 </%def>
360
359
361 <%def name="pullrequest_name(pull_request_id, target_repo_name, short=False)">
360 <%def name="pullrequest_name(pull_request_id, target_repo_name, short=False)">
362 <a href="${h.route_path('pullrequest_show',repo_name=target_repo_name,pull_request_id=pull_request_id)}">
361 <a href="${h.route_path('pullrequest_show',repo_name=target_repo_name,pull_request_id=pull_request_id)}">
363 % if short:
362 % if short:
364 #${pull_request_id}
363 #${pull_request_id}
365 % else:
364 % else:
366 ${_('Pull request #%(pr_number)s') % {'pr_number': pull_request_id,}}
365 ${_('Pull request #%(pr_number)s') % {'pr_number': pull_request_id,}}
367 % endif
366 % endif
368 </a>
367 </a>
369 </%def>
368 </%def>
370
369
371 <%def name="pullrequest_updated_on(updated_on)">
370 <%def name="pullrequest_updated_on(updated_on)">
372 ${h.age_component(h.time_to_utcdatetime(updated_on))}
371 ${h.age_component(h.time_to_utcdatetime(updated_on))}
373 </%def>
372 </%def>
374
373
375 <%def name="pullrequest_author(full_contact)">
374 <%def name="pullrequest_author(full_contact)">
376 ${base.gravatar_with_user(full_contact, 16)}
375 ${base.gravatar_with_user(full_contact, 16)}
377 </%def>
376 </%def>
377
378
379 <%def name="markup_form(form_id, form_text='', help_text=None)">
380
381 <div class="markup-form">
382 <div class="markup-form-area">
383 <div class="markup-form-area-header">
384 <ul class="nav-links clearfix">
385 <li class="active">
386 <a href="#edit-text" tabindex="-1" id="edit-btn_${form_id}">${_('Write')}</a>
387 </li>
388 <li class="">
389 <a href="#preview-text" tabindex="-1" id="preview-btn_${form_id}">${_('Preview')}</a>
390 </li>
391 </ul>
392 </div>
393
394 <div class="markup-form-area-write" style="display: block;">
395 <div id="edit-container_${form_id}">
396 <textarea id="${form_id}" name="${form_id}" class="comment-block-ta ac-input">${form_text if form_text else ''}</textarea>
397 </div>
398 <div id="preview-container_${form_id}" class="clearfix" style="display: none;">
399 <div id="preview-box_${form_id}" class="preview-box"></div>
400 </div>
401 </div>
402
403 <div class="markup-form-area-footer">
404 <div class="toolbar">
405 <div class="toolbar-text">
406 ${(_('Parsed using %s syntax') % (
407 ('<a href="%s">%s</a>' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
408 )
409 )|n}
410 </div>
411 </div>
412 </div>
413 </div>
414
415 <div class="markup-form-footer">
416 % if help_text:
417 <span class="help-block">${help_text}</span>
418 % endif
419 </div>
420 </div>
421 <script type="text/javascript">
422 new MarkupForm('${form_id}');
423 </script>
424
425 </%def>
@@ -1,542 +1,547 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
2
3
3 <%def name="title()">
4 <%def name="title()">
4 ${c.repo_name} ${_('New pull request')}
5 ${c.repo_name} ${_('New pull request')}
5 </%def>
6 </%def>
6
7
7 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
8 ${_('New pull request')}
9 ${_('New pull request')}
9 </%def>
10 </%def>
10
11
11 <%def name="menu_bar_nav()">
12 <%def name="menu_bar_nav()">
12 ${self.menu_items(active='repositories')}
13 ${self.menu_items(active='repositories')}
13 </%def>
14 </%def>
14
15
15 <%def name="menu_bar_subnav()">
16 <%def name="menu_bar_subnav()">
16 ${self.repo_menu(active='showpullrequest')}
17 ${self.repo_menu(active='showpullrequest')}
17 </%def>
18 </%def>
18
19
19 <%def name="main()">
20 <%def name="main()">
20 <div class="box">
21 <div class="box">
21 <div class="title">
22 <div class="title">
22 ${self.repo_page_title(c.rhodecode_db_repo)}
23 ${self.repo_page_title(c.rhodecode_db_repo)}
23 </div>
24 </div>
24
25
25 ${h.secure_form(h.route_path('pullrequest_create', repo_name=c.repo_name, _query=request.GET.mixed()), id='pull_request_form', request=request)}
26 ${h.secure_form(h.route_path('pullrequest_create', repo_name=c.repo_name, _query=request.GET.mixed()), id='pull_request_form', request=request)}
26
27
27 ${self.breadcrumbs()}
28 ${self.breadcrumbs()}
28
29
29 <div class="box pr-summary">
30 <div class="box pr-summary">
30
31
31 <div class="summary-details block-left">
32 <div class="summary-details block-left">
32
33
33
34
34 <div class="pr-details-title">
35 <div class="pr-details-title">
35 ${_('Pull request summary')}
36 ${_('Pull request summary')}
36 </div>
37 </div>
37
38
38 <div class="form" style="padding-top: 10px">
39 <div class="form" style="padding-top: 10px">
39 <!-- fields -->
40 <!-- fields -->
40
41
41 <div class="fields" >
42 <div class="fields" >
42
43
43 <div class="field">
44 <div class="field">
44 <div class="label">
45 <div class="label">
45 <label for="pullrequest_title">${_('Title')}:</label>
46 <label for="pullrequest_title">${_('Title')}:</label>
46 </div>
47 </div>
47 <div class="input">
48 <div class="input">
48 ${h.text('pullrequest_title', c.default_title, class_="medium autogenerated-title")}
49 ${h.text('pullrequest_title', c.default_title, class_="medium autogenerated-title")}
49 </div>
50 </div>
50 </div>
51 </div>
51
52
52 <div class="field">
53 <div class="field">
53 <div class="label label-textarea">
54 <div class="label label-textarea">
54 <label for="pullrequest_desc">${_('Description')}:</label>
55 <label for="pullrequest_desc">${_('Description')}:</label>
55 </div>
56 </div>
56 <div class="textarea text-area editor">
57 <div class="textarea text-area editor">
57 ${h.textarea('pullrequest_desc',size=30, )}
58 ${dt.markup_form('pullrequest_desc')}
58 <span class="help-block">${_('Write a short description on this pull request')}</span>
59 </div>
59 </div>
60 </div>
60 </div>
61
61
62 <div class="field">
62 <div class="field">
63 <div class="label label-textarea">
63 <div class="label label-textarea">
64 <label for="pullrequest_desc">${_('Commit flow')}:</label>
64 <label for="commit_flow">${_('Commit flow')}:</label>
65 </div>
65 </div>
66
66
67 ## TODO: johbo: Abusing the "content" class here to get the
67 ## TODO: johbo: Abusing the "content" class here to get the
68 ## desired effect. Should be replaced by a proper solution.
68 ## desired effect. Should be replaced by a proper solution.
69
69
70 ##ORG
70 ##ORG
71 <div class="content">
71 <div class="content">
72 <strong>${_('Source repository')}:</strong>
72 <strong>${_('Source repository')}:</strong>
73 ${c.rhodecode_db_repo.description}
73 ${c.rhodecode_db_repo.description}
74 </div>
74 </div>
75 <div class="content">
75 <div class="content">
76 ${h.hidden('source_repo')}
76 ${h.hidden('source_repo')}
77 ${h.hidden('source_ref')}
77 ${h.hidden('source_ref')}
78 </div>
78 </div>
79
79
80 ##OTHER, most Probably the PARENT OF THIS FORK
80 ##OTHER, most Probably the PARENT OF THIS FORK
81 <div class="content">
81 <div class="content">
82 ## filled with JS
82 ## filled with JS
83 <div id="target_repo_desc"></div>
83 <div id="target_repo_desc"></div>
84 </div>
84 </div>
85
85
86 <div class="content">
86 <div class="content">
87 ${h.hidden('target_repo')}
87 ${h.hidden('target_repo')}
88 ${h.hidden('target_ref')}
88 ${h.hidden('target_ref')}
89 <span id="target_ref_loading" style="display: none">
89 <span id="target_ref_loading" style="display: none">
90 ${_('Loading refs...')}
90 ${_('Loading refs...')}
91 </span>
91 </span>
92 </div>
92 </div>
93 </div>
93 </div>
94
94
95 <div class="field">
95 <div class="field">
96 <div class="label label-textarea">
96 <div class="label label-textarea">
97 <label for="pullrequest_submit"></label>
97 <label for="pullrequest_submit"></label>
98 </div>
98 </div>
99 <div class="input">
99 <div class="input">
100 <div class="pr-submit-button">
100 <div class="pr-submit-button">
101 <input id="pr_submit" class="btn" name="save" type="submit" value="${_('Submit Pull Request')}">
101 <input id="pr_submit" class="btn" name="save" type="submit" value="${_('Submit Pull Request')}">
102 </div>
102 </div>
103 <div id="pr_open_message"></div>
103 <div id="pr_open_message"></div>
104 </div>
104 </div>
105 </div>
105 </div>
106
106
107 <div class="pr-spacing-container"></div>
107 <div class="pr-spacing-container"></div>
108 </div>
108 </div>
109 </div>
109 </div>
110 </div>
110 </div>
111 <div>
111 <div>
112 ## AUTHOR
112 ## AUTHOR
113 <div class="reviewers-title block-right">
113 <div class="reviewers-title block-right">
114 <div class="pr-details-title">
114 <div class="pr-details-title">
115 ${_('Author of this pull request')}
115 ${_('Author of this pull request')}
116 </div>
116 </div>
117 </div>
117 </div>
118 <div class="block-right pr-details-content reviewers">
118 <div class="block-right pr-details-content reviewers">
119 <ul class="group_members">
119 <ul class="group_members">
120 <li>
120 <li>
121 ${self.gravatar_with_user(c.rhodecode_user.email, 16)}
121 ${self.gravatar_with_user(c.rhodecode_user.email, 16)}
122 </li>
122 </li>
123 </ul>
123 </ul>
124 </div>
124 </div>
125
125
126 ## REVIEW RULES
126 ## REVIEW RULES
127 <div id="review_rules" style="display: none" class="reviewers-title block-right">
127 <div id="review_rules" style="display: none" class="reviewers-title block-right">
128 <div class="pr-details-title">
128 <div class="pr-details-title">
129 ${_('Reviewer rules')}
129 ${_('Reviewer rules')}
130 </div>
130 </div>
131 <div class="pr-reviewer-rules">
131 <div class="pr-reviewer-rules">
132 ## review rules will be appended here, by default reviewers logic
132 ## review rules will be appended here, by default reviewers logic
133 </div>
133 </div>
134 </div>
134 </div>
135
135
136 ## REVIEWERS
136 ## REVIEWERS
137 <div class="reviewers-title block-right">
137 <div class="reviewers-title block-right">
138 <div class="pr-details-title">
138 <div class="pr-details-title">
139 ${_('Pull request reviewers')}
139 ${_('Pull request reviewers')}
140 <span class="calculate-reviewers"> - ${_('loading...')}</span>
140 <span class="calculate-reviewers"> - ${_('loading...')}</span>
141 </div>
141 </div>
142 </div>
142 </div>
143 <div id="reviewers" class="block-right pr-details-content reviewers">
143 <div id="reviewers" class="block-right pr-details-content reviewers">
144 ## members goes here, filled via JS based on initial selection !
144 ## members goes here, filled via JS based on initial selection !
145 <input type="hidden" name="__start__" value="review_members:sequence">
145 <input type="hidden" name="__start__" value="review_members:sequence">
146 <ul id="review_members" class="group_members"></ul>
146 <ul id="review_members" class="group_members"></ul>
147 <input type="hidden" name="__end__" value="review_members:sequence">
147 <input type="hidden" name="__end__" value="review_members:sequence">
148 <div id="add_reviewer_input" class='ac'>
148 <div id="add_reviewer_input" class='ac'>
149 <div class="reviewer_ac">
149 <div class="reviewer_ac">
150 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))}
150 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))}
151 <div id="reviewers_container"></div>
151 <div id="reviewers_container"></div>
152 </div>
152 </div>
153 </div>
153 </div>
154 </div>
154 </div>
155 </div>
155 </div>
156 </div>
156 </div>
157 <div class="box">
157 <div class="box">
158 <div>
158 <div>
159 ## overview pulled by ajax
159 ## overview pulled by ajax
160 <div id="pull_request_overview"></div>
160 <div id="pull_request_overview"></div>
161 </div>
161 </div>
162 </div>
162 </div>
163 ${h.end_form()}
163 ${h.end_form()}
164 </div>
164 </div>
165
165
166 <script type="text/javascript">
166 <script type="text/javascript">
167 $(function(){
167 $(function(){
168 var defaultSourceRepo = '${c.default_repo_data['source_repo_name']}';
168 var defaultSourceRepo = '${c.default_repo_data['source_repo_name']}';
169 var defaultSourceRepoData = ${c.default_repo_data['source_refs_json']|n};
169 var defaultSourceRepoData = ${c.default_repo_data['source_refs_json']|n};
170 var defaultTargetRepo = '${c.default_repo_data['target_repo_name']}';
170 var defaultTargetRepo = '${c.default_repo_data['target_repo_name']}';
171 var defaultTargetRepoData = ${c.default_repo_data['target_refs_json']|n};
171 var defaultTargetRepoData = ${c.default_repo_data['target_refs_json']|n};
172
172
173 var $pullRequestForm = $('#pull_request_form');
173 var $pullRequestForm = $('#pull_request_form');
174 var $pullRequestSubmit = $('#pr_submit', $pullRequestForm);
174 var $pullRequestSubmit = $('#pr_submit', $pullRequestForm);
175 var $sourceRepo = $('#source_repo', $pullRequestForm);
175 var $sourceRepo = $('#source_repo', $pullRequestForm);
176 var $targetRepo = $('#target_repo', $pullRequestForm);
176 var $targetRepo = $('#target_repo', $pullRequestForm);
177 var $sourceRef = $('#source_ref', $pullRequestForm);
177 var $sourceRef = $('#source_ref', $pullRequestForm);
178 var $targetRef = $('#target_ref', $pullRequestForm);
178 var $targetRef = $('#target_ref', $pullRequestForm);
179
179
180 var sourceRepo = function() { return $sourceRepo.eq(0).val() };
180 var sourceRepo = function() { return $sourceRepo.eq(0).val() };
181 var sourceRef = function() { return $sourceRef.eq(0).val().split(':') };
181 var sourceRef = function() { return $sourceRef.eq(0).val().split(':') };
182
182
183 var targetRepo = function() { return $targetRepo.eq(0).val() };
183 var targetRepo = function() { return $targetRepo.eq(0).val() };
184 var targetRef = function() { return $targetRef.eq(0).val().split(':') };
184 var targetRef = function() { return $targetRef.eq(0).val().split(':') };
185
185
186 var calculateContainerWidth = function() {
186 var calculateContainerWidth = function() {
187 var maxWidth = 0;
187 var maxWidth = 0;
188 var repoSelect2Containers = ['#source_repo', '#target_repo'];
188 var repoSelect2Containers = ['#source_repo', '#target_repo'];
189 $.each(repoSelect2Containers, function(idx, value) {
189 $.each(repoSelect2Containers, function(idx, value) {
190 $(value).select2('container').width('auto');
190 $(value).select2('container').width('auto');
191 var curWidth = $(value).select2('container').width();
191 var curWidth = $(value).select2('container').width();
192 if (maxWidth <= curWidth) {
192 if (maxWidth <= curWidth) {
193 maxWidth = curWidth;
193 maxWidth = curWidth;
194 }
194 }
195 $.each(repoSelect2Containers, function(idx, value) {
195 $.each(repoSelect2Containers, function(idx, value) {
196 $(value).select2('container').width(maxWidth + 10);
196 $(value).select2('container').width(maxWidth + 10);
197 });
197 });
198 });
198 });
199 };
199 };
200
200
201 var initRefSelection = function(selectedRef) {
201 var initRefSelection = function(selectedRef) {
202 return function(element, callback) {
202 return function(element, callback) {
203 // translate our select2 id into a text, it's a mapping to show
203 // translate our select2 id into a text, it's a mapping to show
204 // simple label when selecting by internal ID.
204 // simple label when selecting by internal ID.
205 var id, refData;
205 var id, refData;
206 if (selectedRef === undefined || selectedRef === null) {
206 if (selectedRef === undefined || selectedRef === null) {
207 id = element.val();
207 id = element.val();
208 refData = element.val().split(':');
208 refData = element.val().split(':');
209
209
210 if (refData.length !== 3){
210 if (refData.length !== 3){
211 refData = ["", "", ""]
211 refData = ["", "", ""]
212 }
212 }
213 } else {
213 } else {
214 id = selectedRef;
214 id = selectedRef;
215 refData = selectedRef.split(':');
215 refData = selectedRef.split(':');
216 }
216 }
217
217
218 var text = refData[1];
218 var text = refData[1];
219 if (refData[0] === 'rev') {
219 if (refData[0] === 'rev') {
220 text = text.substring(0, 12);
220 text = text.substring(0, 12);
221 }
221 }
222
222
223 var data = {id: id, text: text};
223 var data = {id: id, text: text};
224 callback(data);
224 callback(data);
225 };
225 };
226 };
226 };
227
227
228 var formatRefSelection = function(item) {
228 var formatRefSelection = function(item) {
229 var prefix = '';
229 var prefix = '';
230 var refData = item.id.split(':');
230 var refData = item.id.split(':');
231 if (refData[0] === 'branch') {
231 if (refData[0] === 'branch') {
232 prefix = '<i class="icon-branch"></i>';
232 prefix = '<i class="icon-branch"></i>';
233 }
233 }
234 else if (refData[0] === 'book') {
234 else if (refData[0] === 'book') {
235 prefix = '<i class="icon-bookmark"></i>';
235 prefix = '<i class="icon-bookmark"></i>';
236 }
236 }
237 else if (refData[0] === 'tag') {
237 else if (refData[0] === 'tag') {
238 prefix = '<i class="icon-tag"></i>';
238 prefix = '<i class="icon-tag"></i>';
239 }
239 }
240
240
241 var originalOption = item.element;
241 var originalOption = item.element;
242 return prefix + item.text;
242 return prefix + item.text;
243 };
243 };
244
244
245 // custom code mirror
245 // custom code mirror
246 var codeMirrorInstance = initPullRequestsCodeMirror('#pullrequest_desc');
246 var codeMirrorInstance = $('#pullrequest_desc').get(0).MarkupForm.cm;
247
247
248 reviewersController = new ReviewersController();
248 reviewersController = new ReviewersController();
249
249
250 var queryTargetRepo = function(self, query) {
250 var queryTargetRepo = function(self, query) {
251 // cache ALL results if query is empty
251 // cache ALL results if query is empty
252 var cacheKey = query.term || '__';
252 var cacheKey = query.term || '__';
253 var cachedData = self.cachedDataSource[cacheKey];
253 var cachedData = self.cachedDataSource[cacheKey];
254
254
255 if (cachedData) {
255 if (cachedData) {
256 query.callback({results: cachedData.results});
256 query.callback({results: cachedData.results});
257 } else {
257 } else {
258 $.ajax({
258 $.ajax({
259 url: pyroutes.url('pullrequest_repo_destinations', {'repo_name': templateContext.repo_name}),
259 url: pyroutes.url('pullrequest_repo_destinations', {'repo_name': templateContext.repo_name}),
260 data: {query: query.term},
260 data: {query: query.term},
261 dataType: 'json',
261 dataType: 'json',
262 type: 'GET',
262 type: 'GET',
263 success: function(data) {
263 success: function(data) {
264 self.cachedDataSource[cacheKey] = data;
264 self.cachedDataSource[cacheKey] = data;
265 query.callback({results: data.results});
265 query.callback({results: data.results});
266 },
266 },
267 error: function(data, textStatus, errorThrown) {
267 error: function(data, textStatus, errorThrown) {
268 alert(
268 alert(
269 "Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
269 "Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
270 }
270 }
271 });
271 });
272 }
272 }
273 };
273 };
274
274
275 var queryTargetRefs = function(initialData, query) {
275 var queryTargetRefs = function(initialData, query) {
276 var data = {results: []};
276 var data = {results: []};
277 // filter initialData
277 // filter initialData
278 $.each(initialData, function() {
278 $.each(initialData, function() {
279 var section = this.text;
279 var section = this.text;
280 var children = [];
280 var children = [];
281 $.each(this.children, function() {
281 $.each(this.children, function() {
282 if (query.term.length === 0 ||
282 if (query.term.length === 0 ||
283 this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ) {
283 this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ) {
284 children.push({'id': this.id, 'text': this.text})
284 children.push({'id': this.id, 'text': this.text})
285 }
285 }
286 });
286 });
287 data.results.push({'text': section, 'children': children})
287 data.results.push({'text': section, 'children': children})
288 });
288 });
289 query.callback({results: data.results});
289 query.callback({results: data.results});
290 };
290 };
291
291
292 var loadRepoRefDiffPreview = function() {
292 var loadRepoRefDiffPreview = function() {
293
293
294 var url_data = {
294 var url_data = {
295 'repo_name': targetRepo(),
295 'repo_name': targetRepo(),
296 'target_repo': sourceRepo(),
296 'target_repo': sourceRepo(),
297 'source_ref': targetRef()[2],
297 'source_ref': targetRef()[2],
298 'source_ref_type': 'rev',
298 'source_ref_type': 'rev',
299 'target_ref': sourceRef()[2],
299 'target_ref': sourceRef()[2],
300 'target_ref_type': 'rev',
300 'target_ref_type': 'rev',
301 'merge': true,
301 'merge': true,
302 '_': Date.now() // bypass browser caching
302 '_': Date.now() // bypass browser caching
303 }; // gather the source/target ref and repo here
303 }; // gather the source/target ref and repo here
304
304
305 if (sourceRef().length !== 3 || targetRef().length !== 3) {
305 if (sourceRef().length !== 3 || targetRef().length !== 3) {
306 prButtonLock(true, "${_('Please select source and target')}");
306 prButtonLock(true, "${_('Please select source and target')}");
307 return;
307 return;
308 }
308 }
309 var url = pyroutes.url('repo_compare', url_data);
309 var url = pyroutes.url('repo_compare', url_data);
310
310
311 // lock PR button, so we cannot send PR before it's calculated
311 // lock PR button, so we cannot send PR before it's calculated
312 prButtonLock(true, "${_('Loading compare ...')}", 'compare');
312 prButtonLock(true, "${_('Loading compare ...')}", 'compare');
313
313
314 if (loadRepoRefDiffPreview._currentRequest) {
314 if (loadRepoRefDiffPreview._currentRequest) {
315 loadRepoRefDiffPreview._currentRequest.abort();
315 loadRepoRefDiffPreview._currentRequest.abort();
316 }
316 }
317
317
318 loadRepoRefDiffPreview._currentRequest = $.get(url)
318 loadRepoRefDiffPreview._currentRequest = $.get(url)
319 .error(function(data, textStatus, errorThrown) {
319 .error(function(data, textStatus, errorThrown) {
320 if (textStatus !== 'abort') {
320 if (textStatus !== 'abort') {
321 alert(
321 alert(
322 "Error while processing request.\nError code {0} ({1}).".format(
322 "Error while processing request.\nError code {0} ({1}).".format(
323 data.status, data.statusText));
323 data.status, data.statusText));
324 }
324 }
325
325
326 })
326 })
327 .done(function(data) {
327 .done(function(data) {
328 loadRepoRefDiffPreview._currentRequest = null;
328 loadRepoRefDiffPreview._currentRequest = null;
329 $('#pull_request_overview').html(data);
329 $('#pull_request_overview').html(data);
330
330
331 var commitElements = $(data).find('tr[commit_id]');
331 var commitElements = $(data).find('tr[commit_id]');
332
332
333 var prTitleAndDesc = getTitleAndDescription(
333 var prTitleAndDesc = getTitleAndDescription(
334 sourceRef()[1], commitElements, 5);
334 sourceRef()[1], commitElements, 5);
335
335
336 var title = prTitleAndDesc[0];
336 var title = prTitleAndDesc[0];
337 var proposedDescription = prTitleAndDesc[1];
337 var proposedDescription = prTitleAndDesc[1];
338
338
339 var useGeneratedTitle = (
339 var useGeneratedTitle = (
340 $('#pullrequest_title').hasClass('autogenerated-title') ||
340 $('#pullrequest_title').hasClass('autogenerated-title') ||
341 $('#pullrequest_title').val() === "");
341 $('#pullrequest_title').val() === "");
342
342
343 if (title && useGeneratedTitle) {
343 if (title && useGeneratedTitle) {
344 // use generated title if we haven't specified our own
344 // use generated title if we haven't specified our own
345 $('#pullrequest_title').val(title);
345 $('#pullrequest_title').val(title);
346 $('#pullrequest_title').addClass('autogenerated-title');
346 $('#pullrequest_title').addClass('autogenerated-title');
347
347
348 }
348 }
349
349
350 var useGeneratedDescription = (
350 var useGeneratedDescription = (
351 !codeMirrorInstance._userDefinedDesc ||
351 !codeMirrorInstance._userDefinedValue ||
352 codeMirrorInstance.getValue() === "");
352 codeMirrorInstance.getValue() === "");
353
353
354 if (proposedDescription && useGeneratedDescription) {
354 if (proposedDescription && useGeneratedDescription) {
355 // set proposed content, if we haven't defined our own,
355 // set proposed content, if we haven't defined our own,
356 // or we don't have description written
356 // or we don't have description written
357 codeMirrorInstance._userDefinedDesc = false; // reset state
357 codeMirrorInstance._userDefinedValue = false; // reset state
358 codeMirrorInstance.setValue(proposedDescription);
358 codeMirrorInstance.setValue(proposedDescription);
359 }
359 }
360
360
361 // refresh our codeMirror so events kicks in and it's change aware
362 codeMirrorInstance.refresh();
363
361 var msg = '';
364 var msg = '';
362 if (commitElements.length === 1) {
365 if (commitElements.length === 1) {
363 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 1)}";
366 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 1)}";
364 } else {
367 } else {
365 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 2)}";
368 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 2)}";
366 }
369 }
367
370
368 msg += ' <a id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url);
371 msg += ' <a id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url);
369
372
370 if (commitElements.length) {
373 if (commitElements.length) {
371 var commitsLink = '<a href="#pull_request_overview"><strong>{0}</strong></a>'.format(commitElements.length);
374 var commitsLink = '<a href="#pull_request_overview"><strong>{0}</strong></a>'.format(commitElements.length);
372 prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare');
375 prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare');
373 }
376 }
374 else {
377 else {
375 prButtonLock(true, "${_('There are no commits to merge.')}", 'compare');
378 prButtonLock(true, "${_('There are no commits to merge.')}", 'compare');
376 }
379 }
377
380
378
381
379 });
382 });
380 };
383 };
381
384
382 var Select2Box = function(element, overrides) {
385 var Select2Box = function(element, overrides) {
383 var globalDefaults = {
386 var globalDefaults = {
384 dropdownAutoWidth: true,
387 dropdownAutoWidth: true,
385 containerCssClass: "drop-menu",
388 containerCssClass: "drop-menu",
386 dropdownCssClass: "drop-menu-dropdown"
389 dropdownCssClass: "drop-menu-dropdown"
387 };
390 };
388
391
389 var initSelect2 = function(defaultOptions) {
392 var initSelect2 = function(defaultOptions) {
390 var options = jQuery.extend(globalDefaults, defaultOptions, overrides);
393 var options = jQuery.extend(globalDefaults, defaultOptions, overrides);
391 element.select2(options);
394 element.select2(options);
392 };
395 };
393
396
394 return {
397 return {
395 initRef: function() {
398 initRef: function() {
396 var defaultOptions = {
399 var defaultOptions = {
397 minimumResultsForSearch: 5,
400 minimumResultsForSearch: 5,
398 formatSelection: formatRefSelection
401 formatSelection: formatRefSelection
399 };
402 };
400
403
401 initSelect2(defaultOptions);
404 initSelect2(defaultOptions);
402 },
405 },
403
406
404 initRepo: function(defaultValue, readOnly) {
407 initRepo: function(defaultValue, readOnly) {
405 var defaultOptions = {
408 var defaultOptions = {
406 initSelection : function (element, callback) {
409 initSelection : function (element, callback) {
407 var data = {id: defaultValue, text: defaultValue};
410 var data = {id: defaultValue, text: defaultValue};
408 callback(data);
411 callback(data);
409 }
412 }
410 };
413 };
411
414
412 initSelect2(defaultOptions);
415 initSelect2(defaultOptions);
413
416
414 element.select2('val', defaultSourceRepo);
417 element.select2('val', defaultSourceRepo);
415 if (readOnly === true) {
418 if (readOnly === true) {
416 element.select2('readonly', true);
419 element.select2('readonly', true);
417 }
420 }
418 }
421 }
419 };
422 };
420 };
423 };
421
424
422 var initTargetRefs = function(refsData, selectedRef) {
425 var initTargetRefs = function(refsData, selectedRef) {
423
426
424 Select2Box($targetRef, {
427 Select2Box($targetRef, {
425 placeholder: "${_('Select commit reference')}",
428 placeholder: "${_('Select commit reference')}",
426 query: function(query) {
429 query: function(query) {
427 queryTargetRefs(refsData, query);
430 queryTargetRefs(refsData, query);
428 },
431 },
429 initSelection : initRefSelection(selectedRef)
432 initSelection : initRefSelection(selectedRef)
430 }).initRef();
433 }).initRef();
431
434
432 if (!(selectedRef === undefined)) {
435 if (!(selectedRef === undefined)) {
433 $targetRef.select2('val', selectedRef);
436 $targetRef.select2('val', selectedRef);
434 }
437 }
435 };
438 };
436
439
437 var targetRepoChanged = function(repoData) {
440 var targetRepoChanged = function(repoData) {
438 // generate new DESC of target repo displayed next to select
441 // generate new DESC of target repo displayed next to select
439 var prLink = pyroutes.url('pullrequest_new', {'repo_name': repoData['name']});
442 var prLink = pyroutes.url('pullrequest_new', {'repo_name': repoData['name']});
440 $('#target_repo_desc').html(
443 $('#target_repo_desc').html(
441 "<strong>${_('Target repository')}</strong>: {0}. <a href=\"{1}\">Switch base, and use as source.</a>".format(repoData['description'], prLink)
444 "<strong>${_('Target repository')}</strong>: {0}. <a href=\"{1}\">Switch base, and use as source.</a>".format(repoData['description'], prLink)
442 );
445 );
443
446
444 // generate dynamic select2 for refs.
447 // generate dynamic select2 for refs.
445 initTargetRefs(repoData['refs']['select2_refs'],
448 initTargetRefs(repoData['refs']['select2_refs'],
446 repoData['refs']['selected_ref']);
449 repoData['refs']['selected_ref']);
447
450
448 };
451 };
449
452
450 var sourceRefSelect2 = Select2Box($sourceRef, {
453 var sourceRefSelect2 = Select2Box($sourceRef, {
451 placeholder: "${_('Select commit reference')}",
454 placeholder: "${_('Select commit reference')}",
452 query: function(query) {
455 query: function(query) {
453 var initialData = defaultSourceRepoData['refs']['select2_refs'];
456 var initialData = defaultSourceRepoData['refs']['select2_refs'];
454 queryTargetRefs(initialData, query)
457 queryTargetRefs(initialData, query)
455 },
458 },
456 initSelection: initRefSelection()
459 initSelection: initRefSelection()
457 }
460 }
458 );
461 );
459
462
460 var sourceRepoSelect2 = Select2Box($sourceRepo, {
463 var sourceRepoSelect2 = Select2Box($sourceRepo, {
461 query: function(query) {}
464 query: function(query) {}
462 });
465 });
463
466
464 var targetRepoSelect2 = Select2Box($targetRepo, {
467 var targetRepoSelect2 = Select2Box($targetRepo, {
465 cachedDataSource: {},
468 cachedDataSource: {},
466 query: $.debounce(250, function(query) {
469 query: $.debounce(250, function(query) {
467 queryTargetRepo(this, query);
470 queryTargetRepo(this, query);
468 }),
471 }),
469 formatResult: formatRepoResult
472 formatResult: formatRepoResult
470 });
473 });
471
474
472 sourceRefSelect2.initRef();
475 sourceRefSelect2.initRef();
473
476
474 sourceRepoSelect2.initRepo(defaultSourceRepo, true);
477 sourceRepoSelect2.initRepo(defaultSourceRepo, true);
475
478
476 targetRepoSelect2.initRepo(defaultTargetRepo, false);
479 targetRepoSelect2.initRepo(defaultTargetRepo, false);
477
480
478 $sourceRef.on('change', function(e){
481 $sourceRef.on('change', function(e){
479 loadRepoRefDiffPreview();
482 loadRepoRefDiffPreview();
480 reviewersController.loadDefaultReviewers(
483 reviewersController.loadDefaultReviewers(
481 sourceRepo(), sourceRef(), targetRepo(), targetRef());
484 sourceRepo(), sourceRef(), targetRepo(), targetRef());
482 });
485 });
483
486
484 $targetRef.on('change', function(e){
487 $targetRef.on('change', function(e){
485 loadRepoRefDiffPreview();
488 loadRepoRefDiffPreview();
486 reviewersController.loadDefaultReviewers(
489 reviewersController.loadDefaultReviewers(
487 sourceRepo(), sourceRef(), targetRepo(), targetRef());
490 sourceRepo(), sourceRef(), targetRepo(), targetRef());
488 });
491 });
489
492
490 $targetRepo.on('change', function(e){
493 $targetRepo.on('change', function(e){
491 var repoName = $(this).val();
494 var repoName = $(this).val();
492 calculateContainerWidth();
495 calculateContainerWidth();
493 $targetRef.select2('destroy');
496 $targetRef.select2('destroy');
494 $('#target_ref_loading').show();
497 $('#target_ref_loading').show();
495
498
496 $.ajax({
499 $.ajax({
497 url: pyroutes.url('pullrequest_repo_refs',
500 url: pyroutes.url('pullrequest_repo_refs',
498 {'repo_name': templateContext.repo_name, 'target_repo_name':repoName}),
501 {'repo_name': templateContext.repo_name, 'target_repo_name':repoName}),
499 data: {},
502 data: {},
500 dataType: 'json',
503 dataType: 'json',
501 type: 'GET',
504 type: 'GET',
502 success: function(data) {
505 success: function(data) {
503 $('#target_ref_loading').hide();
506 $('#target_ref_loading').hide();
504 targetRepoChanged(data);
507 targetRepoChanged(data);
505 loadRepoRefDiffPreview();
508 loadRepoRefDiffPreview();
506 },
509 },
507 error: function(data, textStatus, errorThrown) {
510 error: function(data, textStatus, errorThrown) {
508 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
511 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
509 }
512 }
510 })
513 })
511
514
512 });
515 });
513
516
514 $pullRequestForm.on('submit', function(e){
517 $pullRequestForm.on('submit', function(e){
518 // Flush changes into textarea
519 codeMirrorInstance.save();
515 prButtonLock(true, null, 'all');
520 prButtonLock(true, null, 'all');
516 });
521 });
517
522
518 prButtonLock(true, "${_('Please select source and target')}", 'all');
523 prButtonLock(true, "${_('Please select source and target')}", 'all');
519
524
520 // auto-load on init, the target refs select2
525 // auto-load on init, the target refs select2
521 calculateContainerWidth();
526 calculateContainerWidth();
522 targetRepoChanged(defaultTargetRepoData);
527 targetRepoChanged(defaultTargetRepoData);
523
528
524 $('#pullrequest_title').on('keyup', function(e){
529 $('#pullrequest_title').on('keyup', function(e){
525 $(this).removeClass('autogenerated-title');
530 $(this).removeClass('autogenerated-title');
526 });
531 });
527
532
528 % if c.default_source_ref:
533 % if c.default_source_ref:
529 // in case we have a pre-selected value, use it now
534 // in case we have a pre-selected value, use it now
530 $sourceRef.select2('val', '${c.default_source_ref}');
535 $sourceRef.select2('val', '${c.default_source_ref}');
531 // diff preview load
536 // diff preview load
532 loadRepoRefDiffPreview();
537 loadRepoRefDiffPreview();
533 // default reviewers
538 // default reviewers
534 reviewersController.loadDefaultReviewers(
539 reviewersController.loadDefaultReviewers(
535 sourceRepo(), sourceRef(), targetRepo(), targetRef());
540 sourceRepo(), sourceRef(), targetRepo(), targetRef());
536 % endif
541 % endif
537
542
538 ReviewerAutoComplete('#user');
543 ReviewerAutoComplete('#user');
539 });
544 });
540 </script>
545 </script>
541
546
542 </%def>
547 </%def>
@@ -1,856 +1,857 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2 <%namespace name="base" file="/base/base.mako"/>
2 <%namespace name="base" file="/base/base.mako"/>
3 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
3
4
4 <%def name="title()">
5 <%def name="title()">
5 ${_('%s Pull Request #%s') % (c.repo_name, c.pull_request.pull_request_id)}
6 ${_('%s Pull Request #%s') % (c.repo_name, c.pull_request.pull_request_id)}
6 %if c.rhodecode_name:
7 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
8 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
9 %endif
9 </%def>
10 </%def>
10
11
11 <%def name="breadcrumbs_links()">
12 <%def name="breadcrumbs_links()">
12 <span id="pr-title">
13 <span id="pr-title">
13 ${c.pull_request.title}
14 ${c.pull_request.title}
14 %if c.pull_request.is_closed():
15 %if c.pull_request.is_closed():
15 (${_('Closed')})
16 (${_('Closed')})
16 %endif
17 %endif
17 </span>
18 </span>
18 <div id="pr-title-edit" class="input" style="display: none;">
19 <div id="pr-title-edit" class="input" style="display: none;">
19 ${h.text('pullrequest_title', id_="pr-title-input", class_="large", value=c.pull_request.title)}
20 ${h.text('pullrequest_title', id_="pr-title-input", class_="large", value=c.pull_request.title)}
20 </div>
21 </div>
21 </%def>
22 </%def>
22
23
23 <%def name="menu_bar_nav()">
24 <%def name="menu_bar_nav()">
24 ${self.menu_items(active='repositories')}
25 ${self.menu_items(active='repositories')}
25 </%def>
26 </%def>
26
27
27 <%def name="menu_bar_subnav()">
28 <%def name="menu_bar_subnav()">
28 ${self.repo_menu(active='showpullrequest')}
29 ${self.repo_menu(active='showpullrequest')}
29 </%def>
30 </%def>
30
31
31 <%def name="main()">
32 <%def name="main()">
32
33
33 <script type="text/javascript">
34 <script type="text/javascript">
34 // TODO: marcink switch this to pyroutes
35 // TODO: marcink switch this to pyroutes
35 AJAX_COMMENT_DELETE_URL = "${h.route_path('pullrequest_comment_delete',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id,comment_id='__COMMENT_ID__')}";
36 AJAX_COMMENT_DELETE_URL = "${h.route_path('pullrequest_comment_delete',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id,comment_id='__COMMENT_ID__')}";
36 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
37 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
37 </script>
38 </script>
38 <div class="box">
39 <div class="box">
39
40
40 <div class="title">
41 <div class="title">
41 ${self.repo_page_title(c.rhodecode_db_repo)}
42 ${self.repo_page_title(c.rhodecode_db_repo)}
42 </div>
43 </div>
43
44
44 ${self.breadcrumbs()}
45 ${self.breadcrumbs()}
45
46
46 <div class="box pr-summary">
47 <div class="box pr-summary">
47
48
48 <div class="summary-details block-left">
49 <div class="summary-details block-left">
49 <% summary = lambda n:{False:'summary-short'}.get(n) %>
50 <% summary = lambda n:{False:'summary-short'}.get(n) %>
50 <div class="pr-details-title">
51 <div class="pr-details-title">
51 <a href="${h.route_path('pull_requests_global', pull_request_id=c.pull_request.pull_request_id)}">${_('Pull request #%s') % c.pull_request.pull_request_id}</a> ${_('From')} ${h.format_date(c.pull_request.created_on)}
52 <a href="${h.route_path('pull_requests_global', pull_request_id=c.pull_request.pull_request_id)}">${_('Pull request #%s') % c.pull_request.pull_request_id}</a> ${_('From')} ${h.format_date(c.pull_request.created_on)}
52 %if c.allowed_to_update:
53 %if c.allowed_to_update:
53 <div id="delete_pullrequest" class="pull-right action_button ${'' if c.allowed_to_delete else 'disabled' }" style="clear:inherit;padding: 0">
54 <div id="delete_pullrequest" class="pull-right action_button ${'' if c.allowed_to_delete else 'disabled' }" style="clear:inherit;padding: 0">
54 % if c.allowed_to_delete:
55 % if c.allowed_to_delete:
55 ${h.secure_form(h.route_path('pullrequest_delete', repo_name=c.pull_request.target_repo.repo_name, pull_request_id=c.pull_request.pull_request_id), request=request)}
56 ${h.secure_form(h.route_path('pullrequest_delete', repo_name=c.pull_request.target_repo.repo_name, pull_request_id=c.pull_request.pull_request_id), request=request)}
56 ${h.submit('remove_%s' % c.pull_request.pull_request_id, _('Delete'),
57 ${h.submit('remove_%s' % c.pull_request.pull_request_id, _('Delete'),
57 class_="btn btn-link btn-danger no-margin",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")}
58 class_="btn btn-link btn-danger no-margin",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")}
58 ${h.end_form()}
59 ${h.end_form()}
59 % else:
60 % else:
60 ${_('Delete')}
61 ${_('Delete')}
61 % endif
62 % endif
62 </div>
63 </div>
63 <div id="open_edit_pullrequest" class="pull-right action_button">${_('Edit')}</div>
64 <div id="open_edit_pullrequest" class="pull-right action_button">${_('Edit')}</div>
64 <div id="close_edit_pullrequest" class="pull-right action_button" style="display: none;padding: 0">${_('Cancel')}</div>
65 <div id="close_edit_pullrequest" class="pull-right action_button" style="display: none;padding: 0">${_('Cancel')}</div>
65 %endif
66 %endif
66 </div>
67 </div>
67
68
68 <div id="summary" class="fields pr-details-content">
69 <div id="summary" class="fields pr-details-content">
69 <div class="field">
70 <div class="field">
70 <div class="label-summary">
71 <div class="label-summary">
71 <label>${_('Source')}:</label>
72 <label>${_('Source')}:</label>
72 </div>
73 </div>
73 <div class="input">
74 <div class="input">
74 <div class="pr-origininfo">
75 <div class="pr-origininfo">
75 ## branch link is only valid if it is a branch
76 ## branch link is only valid if it is a branch
76 <span class="tag">
77 <span class="tag">
77 %if c.pull_request.source_ref_parts.type == 'branch':
78 %if c.pull_request.source_ref_parts.type == 'branch':
78 <a href="${h.route_path('repo_changelog', repo_name=c.pull_request.source_repo.repo_name, _query=dict(branch=c.pull_request.source_ref_parts.name))}">${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}</a>
79 <a href="${h.route_path('repo_changelog', repo_name=c.pull_request.source_repo.repo_name, _query=dict(branch=c.pull_request.source_ref_parts.name))}">${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}</a>
79 %else:
80 %else:
80 ${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}
81 ${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}
81 %endif
82 %endif
82 </span>
83 </span>
83 <span class="clone-url">
84 <span class="clone-url">
84 <a href="${h.route_path('repo_summary', repo_name=c.pull_request.source_repo.repo_name)}">${c.pull_request.source_repo.clone_url()}</a>
85 <a href="${h.route_path('repo_summary', repo_name=c.pull_request.source_repo.repo_name)}">${c.pull_request.source_repo.clone_url()}</a>
85 </span>
86 </span>
86 <br/>
87 <br/>
87 % if c.ancestor_commit:
88 % if c.ancestor_commit:
88 ${_('Common ancestor')}:
89 ${_('Common ancestor')}:
89 <code><a href="${h.route_path('repo_commit', repo_name=c.target_repo.repo_name, commit_id=c.ancestor_commit.raw_id)}">${h.show_id(c.ancestor_commit)}</a></code>
90 <code><a href="${h.route_path('repo_commit', repo_name=c.target_repo.repo_name, commit_id=c.ancestor_commit.raw_id)}">${h.show_id(c.ancestor_commit)}</a></code>
90 % endif
91 % endif
91 </div>
92 </div>
92 %if h.is_hg(c.pull_request.source_repo):
93 %if h.is_hg(c.pull_request.source_repo):
93 <% clone_url = 'hg pull -r {} {}'.format(h.short_id(c.source_ref), c.pull_request.source_repo.clone_url()) %>
94 <% clone_url = 'hg pull -r {} {}'.format(h.short_id(c.source_ref), c.pull_request.source_repo.clone_url()) %>
94 %elif h.is_git(c.pull_request.source_repo):
95 %elif h.is_git(c.pull_request.source_repo):
95 <% clone_url = 'git pull {} {}'.format(c.pull_request.source_repo.clone_url(), c.pull_request.source_ref_parts.name) %>
96 <% clone_url = 'git pull {} {}'.format(c.pull_request.source_repo.clone_url(), c.pull_request.source_ref_parts.name) %>
96 %endif
97 %endif
97
98
98 <div class="">
99 <div class="">
99 <input type="text" class="input-monospace pr-pullinfo" value="${clone_url}" readonly="readonly">
100 <input type="text" class="input-monospace pr-pullinfo" value="${clone_url}" readonly="readonly">
100 <i class="tooltip icon-clipboard clipboard-action pull-right pr-pullinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the pull url')}"></i>
101 <i class="tooltip icon-clipboard clipboard-action pull-right pr-pullinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the pull url')}"></i>
101 </div>
102 </div>
102
103
103 </div>
104 </div>
104 </div>
105 </div>
105 <div class="field">
106 <div class="field">
106 <div class="label-summary">
107 <div class="label-summary">
107 <label>${_('Target')}:</label>
108 <label>${_('Target')}:</label>
108 </div>
109 </div>
109 <div class="input">
110 <div class="input">
110 <div class="pr-targetinfo">
111 <div class="pr-targetinfo">
111 ## branch link is only valid if it is a branch
112 ## branch link is only valid if it is a branch
112 <span class="tag">
113 <span class="tag">
113 %if c.pull_request.target_ref_parts.type == 'branch':
114 %if c.pull_request.target_ref_parts.type == 'branch':
114 <a href="${h.route_path('repo_changelog', repo_name=c.pull_request.target_repo.repo_name, _query=dict(branch=c.pull_request.target_ref_parts.name))}">${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}</a>
115 <a href="${h.route_path('repo_changelog', repo_name=c.pull_request.target_repo.repo_name, _query=dict(branch=c.pull_request.target_ref_parts.name))}">${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}</a>
115 %else:
116 %else:
116 ${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}
117 ${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}
117 %endif
118 %endif
118 </span>
119 </span>
119 <span class="clone-url">
120 <span class="clone-url">
120 <a href="${h.route_path('repo_summary', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.clone_url()}</a>
121 <a href="${h.route_path('repo_summary', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.clone_url()}</a>
121 </span>
122 </span>
122 </div>
123 </div>
123 </div>
124 </div>
124 </div>
125 </div>
125
126
126 ## Link to the shadow repository.
127 ## Link to the shadow repository.
127 <div class="field">
128 <div class="field">
128 <div class="label-summary">
129 <div class="label-summary">
129 <label>${_('Merge')}:</label>
130 <label>${_('Merge')}:</label>
130 </div>
131 </div>
131 <div class="input">
132 <div class="input">
132 % if not c.pull_request.is_closed() and c.pull_request.shadow_merge_ref:
133 % if not c.pull_request.is_closed() and c.pull_request.shadow_merge_ref:
133 %if h.is_hg(c.pull_request.target_repo):
134 %if h.is_hg(c.pull_request.target_repo):
134 <% clone_url = 'hg clone --update {} {} pull-request-{}'.format(c.pull_request.shadow_merge_ref.name, c.shadow_clone_url, c.pull_request.pull_request_id) %>
135 <% clone_url = 'hg clone --update {} {} pull-request-{}'.format(c.pull_request.shadow_merge_ref.name, c.shadow_clone_url, c.pull_request.pull_request_id) %>
135 %elif h.is_git(c.pull_request.target_repo):
136 %elif h.is_git(c.pull_request.target_repo):
136 <% clone_url = 'git clone --branch {} {} pull-request-{}'.format(c.pull_request.shadow_merge_ref.name, c.shadow_clone_url, c.pull_request.pull_request_id) %>
137 <% clone_url = 'git clone --branch {} {} pull-request-{}'.format(c.pull_request.shadow_merge_ref.name, c.shadow_clone_url, c.pull_request.pull_request_id) %>
137 %endif
138 %endif
138 <div class="">
139 <div class="">
139 <input type="text" class="input-monospace pr-mergeinfo" value="${clone_url}" readonly="readonly">
140 <input type="text" class="input-monospace pr-mergeinfo" value="${clone_url}" readonly="readonly">
140 <i class="tooltip icon-clipboard clipboard-action pull-right pr-mergeinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the clone url')}"></i>
141 <i class="tooltip icon-clipboard clipboard-action pull-right pr-mergeinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the clone url')}"></i>
141 </div>
142 </div>
142 % else:
143 % else:
143 <div class="">
144 <div class="">
144 ${_('Shadow repository data not available')}.
145 ${_('Shadow repository data not available')}.
145 </div>
146 </div>
146 % endif
147 % endif
147 </div>
148 </div>
148 </div>
149 </div>
149
150
150 <div class="field">
151 <div class="field">
151 <div class="label-summary">
152 <div class="label-summary">
152 <label>${_('Review')}:</label>
153 <label>${_('Review')}:</label>
153 </div>
154 </div>
154 <div class="input">
155 <div class="input">
155 %if c.pull_request_review_status:
156 %if c.pull_request_review_status:
156 <div class="${'flag_status %s' % c.pull_request_review_status} tooltip pull-left"></div>
157 <div class="${'flag_status %s' % c.pull_request_review_status} tooltip pull-left"></div>
157 <span class="changeset-status-lbl tooltip">
158 <span class="changeset-status-lbl tooltip">
158 %if c.pull_request.is_closed():
159 %if c.pull_request.is_closed():
159 ${_('Closed')},
160 ${_('Closed')},
160 %endif
161 %endif
161 ${h.commit_status_lbl(c.pull_request_review_status)}
162 ${h.commit_status_lbl(c.pull_request_review_status)}
162 </span>
163 </span>
163 - ${_ungettext('calculated based on %s reviewer vote', 'calculated based on %s reviewers votes', len(c.pull_request_reviewers)) % len(c.pull_request_reviewers)}
164 - ${_ungettext('calculated based on %s reviewer vote', 'calculated based on %s reviewers votes', len(c.pull_request_reviewers)) % len(c.pull_request_reviewers)}
164 %endif
165 %endif
165 </div>
166 </div>
166 </div>
167 </div>
167 <div class="field">
168 <div class="field">
168 <div class="pr-description-label label-summary">
169 <div class="pr-description-label label-summary">
169 <label>${_('Description')}:</label>
170 <label>${_('Description')}:</label>
170 </div>
171 </div>
171 <div id="pr-desc" class="input">
172 <div id="pr-desc" class="input">
172 <div class="pr-description">${h.urlify_commit_message(c.pull_request.description, c.repo_name)}</div>
173 <div class="pr-description">${h.render(c.pull_request.description, renderer=c.visual.default_renderer)}</div>
173 </div>
174 </div>
174 <div id="pr-desc-edit" class="input textarea editor" style="display: none;">
175 <div id="pr-desc-edit" class="input textarea editor" style="display: none;">
175 <textarea id="pr-description-input" size="30">${c.pull_request.description}</textarea>
176 ${dt.markup_form('pr-description-input', form_text=c.pull_request.description)}
176 </div>
177 </div>
177 </div>
178 </div>
178
179
179 <div class="field">
180 <div class="field">
180 <div class="label-summary">
181 <div class="label-summary">
181 <label>${_('Versions')}:</label>
182 <label>${_('Versions')}:</label>
182 </div>
183 </div>
183
184
184 <% outdated_comm_count_ver = len(c.inline_versions[None]['outdated']) %>
185 <% outdated_comm_count_ver = len(c.inline_versions[None]['outdated']) %>
185 <% general_outdated_comm_count_ver = len(c.comment_versions[None]['outdated']) %>
186 <% general_outdated_comm_count_ver = len(c.comment_versions[None]['outdated']) %>
186
187
187 <div class="pr-versions">
188 <div class="pr-versions">
188 % if c.show_version_changes:
189 % if c.show_version_changes:
189 <% outdated_comm_count_ver = len(c.inline_versions[c.at_version_num]['outdated']) %>
190 <% outdated_comm_count_ver = len(c.inline_versions[c.at_version_num]['outdated']) %>
190 <% general_outdated_comm_count_ver = len(c.comment_versions[c.at_version_num]['outdated']) %>
191 <% general_outdated_comm_count_ver = len(c.comment_versions[c.at_version_num]['outdated']) %>
191 <a id="show-pr-versions" class="input" onclick="return versionController.toggleVersionView(this)" href="#show-pr-versions"
192 <a id="show-pr-versions" class="input" onclick="return versionController.toggleVersionView(this)" href="#show-pr-versions"
192 data-toggle-on="${_ungettext('{} version available for this pull request, show it.', '{} versions available for this pull request, show them.', len(c.versions)).format(len(c.versions))}"
193 data-toggle-on="${_ungettext('{} version available for this pull request, show it.', '{} versions available for this pull request, show them.', len(c.versions)).format(len(c.versions))}"
193 data-toggle-off="${_('Hide all versions of this pull request')}">
194 data-toggle-off="${_('Hide all versions of this pull request')}">
194 ${_ungettext('{} version available for this pull request, show it.', '{} versions available for this pull request, show them.', len(c.versions)).format(len(c.versions))}
195 ${_ungettext('{} version available for this pull request, show it.', '{} versions available for this pull request, show them.', len(c.versions)).format(len(c.versions))}
195 </a>
196 </a>
196 <table>
197 <table>
197 ## SHOW ALL VERSIONS OF PR
198 ## SHOW ALL VERSIONS OF PR
198 <% ver_pr = None %>
199 <% ver_pr = None %>
199
200
200 % for data in reversed(list(enumerate(c.versions, 1))):
201 % for data in reversed(list(enumerate(c.versions, 1))):
201 <% ver_pos = data[0] %>
202 <% ver_pos = data[0] %>
202 <% ver = data[1] %>
203 <% ver = data[1] %>
203 <% ver_pr = ver.pull_request_version_id %>
204 <% ver_pr = ver.pull_request_version_id %>
204 <% display_row = '' if c.at_version and (c.at_version_num == ver_pr or c.from_version_num == ver_pr) else 'none' %>
205 <% display_row = '' if c.at_version and (c.at_version_num == ver_pr or c.from_version_num == ver_pr) else 'none' %>
205
206
206 <tr class="version-pr" style="display: ${display_row}">
207 <tr class="version-pr" style="display: ${display_row}">
207 <td>
208 <td>
208 <code>
209 <code>
209 <a href="${request.current_route_path(_query=dict(version=ver_pr or 'latest'))}">v${ver_pos}</a>
210 <a href="${request.current_route_path(_query=dict(version=ver_pr or 'latest'))}">v${ver_pos}</a>
210 </code>
211 </code>
211 </td>
212 </td>
212 <td>
213 <td>
213 <input ${'checked="checked"' if c.from_version_num == ver_pr else ''} class="compare-radio-button" type="radio" name="ver_source" value="${ver_pr or 'latest'}" data-ver-pos="${ver_pos}"/>
214 <input ${'checked="checked"' if c.from_version_num == ver_pr else ''} class="compare-radio-button" type="radio" name="ver_source" value="${ver_pr or 'latest'}" data-ver-pos="${ver_pos}"/>
214 <input ${'checked="checked"' if c.at_version_num == ver_pr else ''} class="compare-radio-button" type="radio" name="ver_target" value="${ver_pr or 'latest'}" data-ver-pos="${ver_pos}"/>
215 <input ${'checked="checked"' if c.at_version_num == ver_pr else ''} class="compare-radio-button" type="radio" name="ver_target" value="${ver_pr or 'latest'}" data-ver-pos="${ver_pos}"/>
215 </td>
216 </td>
216 <td>
217 <td>
217 <% review_status = c.review_versions[ver_pr].status if ver_pr in c.review_versions else 'not_reviewed' %>
218 <% review_status = c.review_versions[ver_pr].status if ver_pr in c.review_versions else 'not_reviewed' %>
218 <div class="${'flag_status %s' % review_status} tooltip pull-left" title="${_('Your review status at this version')}">
219 <div class="${'flag_status %s' % review_status} tooltip pull-left" title="${_('Your review status at this version')}">
219 </div>
220 </div>
220 </td>
221 </td>
221 <td>
222 <td>
222 % if c.at_version_num != ver_pr:
223 % if c.at_version_num != ver_pr:
223 <i class="icon-comment"></i>
224 <i class="icon-comment"></i>
224 <code class="tooltip" title="${_('Comment from pull request version v{0}, general:{1} inline:{2}').format(ver_pos, len(c.comment_versions[ver_pr]['at']), len(c.inline_versions[ver_pr]['at']))}">
225 <code class="tooltip" title="${_('Comment from pull request version v{0}, general:{1} inline:{2}').format(ver_pos, len(c.comment_versions[ver_pr]['at']), len(c.inline_versions[ver_pr]['at']))}">
225 G:${len(c.comment_versions[ver_pr]['at'])} / I:${len(c.inline_versions[ver_pr]['at'])}
226 G:${len(c.comment_versions[ver_pr]['at'])} / I:${len(c.inline_versions[ver_pr]['at'])}
226 </code>
227 </code>
227 % endif
228 % endif
228 </td>
229 </td>
229 <td>
230 <td>
230 ##<code>${ver.source_ref_parts.commit_id[:6]}</code>
231 ##<code>${ver.source_ref_parts.commit_id[:6]}</code>
231 </td>
232 </td>
232 <td>
233 <td>
233 ${h.age_component(ver.updated_on, time_is_local=True)}
234 ${h.age_component(ver.updated_on, time_is_local=True)}
234 </td>
235 </td>
235 </tr>
236 </tr>
236 % endfor
237 % endfor
237
238
238 <tr>
239 <tr>
239 <td colspan="6">
240 <td colspan="6">
240 <button id="show-version-diff" onclick="return versionController.showVersionDiff()" class="btn btn-sm" style="display: none"
241 <button id="show-version-diff" onclick="return versionController.showVersionDiff()" class="btn btn-sm" style="display: none"
241 data-label-text-locked="${_('select versions to show changes')}"
242 data-label-text-locked="${_('select versions to show changes')}"
242 data-label-text-diff="${_('show changes between versions')}"
243 data-label-text-diff="${_('show changes between versions')}"
243 data-label-text-show="${_('show pull request for this version')}"
244 data-label-text-show="${_('show pull request for this version')}"
244 >
245 >
245 ${_('select versions to show changes')}
246 ${_('select versions to show changes')}
246 </button>
247 </button>
247 </td>
248 </td>
248 </tr>
249 </tr>
249
250
250 ## show comment/inline comments summary
251 ## show comment/inline comments summary
251 <%def name="comments_summary()">
252 <%def name="comments_summary()">
252 <tr>
253 <tr>
253 <td colspan="6" class="comments-summary-td">
254 <td colspan="6" class="comments-summary-td">
254
255
255 % if c.at_version:
256 % if c.at_version:
256 <% inline_comm_count_ver = len(c.inline_versions[c.at_version_num]['display']) %>
257 <% inline_comm_count_ver = len(c.inline_versions[c.at_version_num]['display']) %>
257 <% general_comm_count_ver = len(c.comment_versions[c.at_version_num]['display']) %>
258 <% general_comm_count_ver = len(c.comment_versions[c.at_version_num]['display']) %>
258 ${_('Comments at this version')}:
259 ${_('Comments at this version')}:
259 % else:
260 % else:
260 <% inline_comm_count_ver = len(c.inline_versions[c.at_version_num]['until']) %>
261 <% inline_comm_count_ver = len(c.inline_versions[c.at_version_num]['until']) %>
261 <% general_comm_count_ver = len(c.comment_versions[c.at_version_num]['until']) %>
262 <% general_comm_count_ver = len(c.comment_versions[c.at_version_num]['until']) %>
262 ${_('Comments for this pull request')}:
263 ${_('Comments for this pull request')}:
263 % endif
264 % endif
264
265
265
266
266 %if general_comm_count_ver:
267 %if general_comm_count_ver:
267 <a href="#comments">${_("%d General ") % general_comm_count_ver}</a>
268 <a href="#comments">${_("%d General ") % general_comm_count_ver}</a>
268 %else:
269 %else:
269 ${_("%d General ") % general_comm_count_ver}
270 ${_("%d General ") % general_comm_count_ver}
270 %endif
271 %endif
271
272
272 %if inline_comm_count_ver:
273 %if inline_comm_count_ver:
273 , <a href="#" onclick="return Rhodecode.comments.nextComment();" id="inline-comments-counter">${_("%d Inline") % inline_comm_count_ver}</a>
274 , <a href="#" onclick="return Rhodecode.comments.nextComment();" id="inline-comments-counter">${_("%d Inline") % inline_comm_count_ver}</a>
274 %else:
275 %else:
275 , ${_("%d Inline") % inline_comm_count_ver}
276 , ${_("%d Inline") % inline_comm_count_ver}
276 %endif
277 %endif
277
278
278 %if outdated_comm_count_ver:
279 %if outdated_comm_count_ver:
279 , <a href="#" onclick="showOutdated(); Rhodecode.comments.nextOutdatedComment(); return false;">${_("%d Outdated") % outdated_comm_count_ver}</a>
280 , <a href="#" onclick="showOutdated(); Rhodecode.comments.nextOutdatedComment(); return false;">${_("%d Outdated") % outdated_comm_count_ver}</a>
280 <a href="#" class="showOutdatedComments" onclick="showOutdated(this); return false;"> | ${_('show outdated comments')}</a>
281 <a href="#" class="showOutdatedComments" onclick="showOutdated(this); return false;"> | ${_('show outdated comments')}</a>
281 <a href="#" class="hideOutdatedComments" style="display: none" onclick="hideOutdated(this); return false;"> | ${_('hide outdated comments')}</a>
282 <a href="#" class="hideOutdatedComments" style="display: none" onclick="hideOutdated(this); return false;"> | ${_('hide outdated comments')}</a>
282 %else:
283 %else:
283 , ${_("%d Outdated") % outdated_comm_count_ver}
284 , ${_("%d Outdated") % outdated_comm_count_ver}
284 %endif
285 %endif
285 </td>
286 </td>
286 </tr>
287 </tr>
287 </%def>
288 </%def>
288 ${comments_summary()}
289 ${comments_summary()}
289 </table>
290 </table>
290 % else:
291 % else:
291 <div class="input">
292 <div class="input">
292 ${_('Pull request versions not available')}.
293 ${_('Pull request versions not available')}.
293 </div>
294 </div>
294 <div>
295 <div>
295 <table>
296 <table>
296 ${comments_summary()}
297 ${comments_summary()}
297 </table>
298 </table>
298 </div>
299 </div>
299 % endif
300 % endif
300 </div>
301 </div>
301 </div>
302 </div>
302
303
303 <div id="pr-save" class="field" style="display: none;">
304 <div id="pr-save" class="field" style="display: none;">
304 <div class="label-summary"></div>
305 <div class="label-summary"></div>
305 <div class="input">
306 <div class="input">
306 <span id="edit_pull_request" class="btn btn-small no-margin">${_('Save Changes')}</span>
307 <span id="edit_pull_request" class="btn btn-small no-margin">${_('Save Changes')}</span>
307 </div>
308 </div>
308 </div>
309 </div>
309 </div>
310 </div>
310 </div>
311 </div>
311 <div>
312 <div>
312 ## AUTHOR
313 ## AUTHOR
313 <div class="reviewers-title block-right">
314 <div class="reviewers-title block-right">
314 <div class="pr-details-title">
315 <div class="pr-details-title">
315 ${_('Author of this pull request')}
316 ${_('Author of this pull request')}
316 </div>
317 </div>
317 </div>
318 </div>
318 <div class="block-right pr-details-content reviewers">
319 <div class="block-right pr-details-content reviewers">
319 <ul class="group_members">
320 <ul class="group_members">
320 <li>
321 <li>
321 ${self.gravatar_with_user(c.pull_request.author.email, 16)}
322 ${self.gravatar_with_user(c.pull_request.author.email, 16)}
322 </li>
323 </li>
323 </ul>
324 </ul>
324 </div>
325 </div>
325
326
326 ## REVIEW RULES
327 ## REVIEW RULES
327 <div id="review_rules" style="display: none" class="reviewers-title block-right">
328 <div id="review_rules" style="display: none" class="reviewers-title block-right">
328 <div class="pr-details-title">
329 <div class="pr-details-title">
329 ${_('Reviewer rules')}
330 ${_('Reviewer rules')}
330 %if c.allowed_to_update:
331 %if c.allowed_to_update:
331 <span id="close_edit_reviewers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
332 <span id="close_edit_reviewers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
332 %endif
333 %endif
333 </div>
334 </div>
334 <div class="pr-reviewer-rules">
335 <div class="pr-reviewer-rules">
335 ## review rules will be appended here, by default reviewers logic
336 ## review rules will be appended here, by default reviewers logic
336 </div>
337 </div>
337 <input id="review_data" type="hidden" name="review_data" value="">
338 <input id="review_data" type="hidden" name="review_data" value="">
338 </div>
339 </div>
339
340
340 ## REVIEWERS
341 ## REVIEWERS
341 <div class="reviewers-title block-right">
342 <div class="reviewers-title block-right">
342 <div class="pr-details-title">
343 <div class="pr-details-title">
343 ${_('Pull request reviewers')}
344 ${_('Pull request reviewers')}
344 %if c.allowed_to_update:
345 %if c.allowed_to_update:
345 <span id="open_edit_reviewers" class="block-right action_button last-item">${_('Edit')}</span>
346 <span id="open_edit_reviewers" class="block-right action_button last-item">${_('Edit')}</span>
346 %endif
347 %endif
347 </div>
348 </div>
348 </div>
349 </div>
349 <div id="reviewers" class="block-right pr-details-content reviewers">
350 <div id="reviewers" class="block-right pr-details-content reviewers">
350
351
351 ## members redering block
352 ## members redering block
352 <input type="hidden" name="__start__" value="review_members:sequence">
353 <input type="hidden" name="__start__" value="review_members:sequence">
353 <ul id="review_members" class="group_members">
354 <ul id="review_members" class="group_members">
354
355
355 % for review_obj, member, reasons, mandatory, status in c.pull_request_reviewers:
356 % for review_obj, member, reasons, mandatory, status in c.pull_request_reviewers:
356 <script>
357 <script>
357 var member = ${h.json.dumps(h.reviewer_as_json(member, reasons=reasons, mandatory=mandatory, user_group=review_obj.rule_user_group_data()))|n};
358 var member = ${h.json.dumps(h.reviewer_as_json(member, reasons=reasons, mandatory=mandatory, user_group=review_obj.rule_user_group_data()))|n};
358 var status = "${(status[0][1].status if status else 'not_reviewed')}";
359 var status = "${(status[0][1].status if status else 'not_reviewed')}";
359 var status_lbl = "${h.commit_status_lbl(status[0][1].status if status else 'not_reviewed')}";
360 var status_lbl = "${h.commit_status_lbl(status[0][1].status if status else 'not_reviewed')}";
360 var allowed_to_update = ${h.json.dumps(c.allowed_to_update)};
361 var allowed_to_update = ${h.json.dumps(c.allowed_to_update)};
361
362
362 var entry = renderTemplate('reviewMemberEntry', {
363 var entry = renderTemplate('reviewMemberEntry', {
363 'member': member,
364 'member': member,
364 'mandatory': member.mandatory,
365 'mandatory': member.mandatory,
365 'reasons': member.reasons,
366 'reasons': member.reasons,
366 'allowed_to_update': allowed_to_update,
367 'allowed_to_update': allowed_to_update,
367 'review_status': status,
368 'review_status': status,
368 'review_status_label': status_lbl,
369 'review_status_label': status_lbl,
369 'user_group': member.user_group,
370 'user_group': member.user_group,
370 'create': false
371 'create': false
371 });
372 });
372 $('#review_members').append(entry)
373 $('#review_members').append(entry)
373 </script>
374 </script>
374
375
375 % endfor
376 % endfor
376
377
377 </ul>
378 </ul>
378 <input type="hidden" name="__end__" value="review_members:sequence">
379 <input type="hidden" name="__end__" value="review_members:sequence">
379 ## end members redering block
380 ## end members redering block
380
381
381 %if not c.pull_request.is_closed():
382 %if not c.pull_request.is_closed():
382 <div id="add_reviewer" class="ac" style="display: none;">
383 <div id="add_reviewer" class="ac" style="display: none;">
383 %if c.allowed_to_update:
384 %if c.allowed_to_update:
384 % if not c.forbid_adding_reviewers:
385 % if not c.forbid_adding_reviewers:
385 <div id="add_reviewer_input" class="reviewer_ac">
386 <div id="add_reviewer_input" class="reviewer_ac">
386 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))}
387 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))}
387 <div id="reviewers_container"></div>
388 <div id="reviewers_container"></div>
388 </div>
389 </div>
389 % endif
390 % endif
390 <div class="pull-right">
391 <div class="pull-right">
391 <button id="update_pull_request" class="btn btn-small no-margin">${_('Save Changes')}</button>
392 <button id="update_pull_request" class="btn btn-small no-margin">${_('Save Changes')}</button>
392 </div>
393 </div>
393 %endif
394 %endif
394 </div>
395 </div>
395 %endif
396 %endif
396 </div>
397 </div>
397 </div>
398 </div>
398 </div>
399 </div>
399 <div class="box">
400 <div class="box">
400 ##DIFF
401 ##DIFF
401 <div class="table" >
402 <div class="table" >
402 <div id="changeset_compare_view_content">
403 <div id="changeset_compare_view_content">
403 ##CS
404 ##CS
404 % if c.missing_requirements:
405 % if c.missing_requirements:
405 <div class="box">
406 <div class="box">
406 <div class="alert alert-warning">
407 <div class="alert alert-warning">
407 <div>
408 <div>
408 <strong>${_('Missing requirements:')}</strong>
409 <strong>${_('Missing requirements:')}</strong>
409 ${_('These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.')}
410 ${_('These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.')}
410 </div>
411 </div>
411 </div>
412 </div>
412 </div>
413 </div>
413 % elif c.missing_commits:
414 % elif c.missing_commits:
414 <div class="box">
415 <div class="box">
415 <div class="alert alert-warning">
416 <div class="alert alert-warning">
416 <div>
417 <div>
417 <strong>${_('Missing commits')}:</strong>
418 <strong>${_('Missing commits')}:</strong>
418 ${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')}
419 ${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')}
419 ${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')}
420 ${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')}
420 ${_('Consider doing a {force_refresh_url} in case you think this is an error.').format(force_refresh_url=h.link_to('force refresh', h.current_route_path(request, force_refresh='1')))|n}
421 ${_('Consider doing a {force_refresh_url} in case you think this is an error.').format(force_refresh_url=h.link_to('force refresh', h.current_route_path(request, force_refresh='1')))|n}
421 </div>
422 </div>
422 </div>
423 </div>
423 </div>
424 </div>
424 % endif
425 % endif
425
426
426 <div class="compare_view_commits_title">
427 <div class="compare_view_commits_title">
427 % if not c.compare_mode:
428 % if not c.compare_mode:
428
429
429 % if c.at_version_pos:
430 % if c.at_version_pos:
430 <h4>
431 <h4>
431 ${_('Showing changes at v%d, commenting is disabled.') % c.at_version_pos}
432 ${_('Showing changes at v%d, commenting is disabled.') % c.at_version_pos}
432 </h4>
433 </h4>
433 % endif
434 % endif
434
435
435 <div class="pull-left">
436 <div class="pull-left">
436 <div class="btn-group">
437 <div class="btn-group">
437 <a
438 <a
438 class="btn"
439 class="btn"
439 href="#"
440 href="#"
440 onclick="$('.compare_select').show();$('.compare_select_hidden').hide(); return false">
441 onclick="$('.compare_select').show();$('.compare_select_hidden').hide(); return false">
441 ${_ungettext('Expand %s commit','Expand %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
442 ${_ungettext('Expand %s commit','Expand %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
442 </a>
443 </a>
443 <a
444 <a
444 class="btn"
445 class="btn"
445 href="#"
446 href="#"
446 onclick="$('.compare_select').hide();$('.compare_select_hidden').show(); return false">
447 onclick="$('.compare_select').hide();$('.compare_select_hidden').show(); return false">
447 ${_ungettext('Collapse %s commit','Collapse %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
448 ${_ungettext('Collapse %s commit','Collapse %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
448 </a>
449 </a>
449 </div>
450 </div>
450 </div>
451 </div>
451
452
452 <div class="pull-right">
453 <div class="pull-right">
453 % if c.allowed_to_update and not c.pull_request.is_closed():
454 % if c.allowed_to_update and not c.pull_request.is_closed():
454 <a id="update_commits" class="btn btn-primary no-margin pull-right">${_('Update commits')}</a>
455 <a id="update_commits" class="btn btn-primary no-margin pull-right">${_('Update commits')}</a>
455 % else:
456 % else:
456 <a class="tooltip btn disabled pull-right" disabled="disabled" title="${_('Update is disabled for current view')}">${_('Update commits')}</a>
457 <a class="tooltip btn disabled pull-right" disabled="disabled" title="${_('Update is disabled for current view')}">${_('Update commits')}</a>
457 % endif
458 % endif
458
459
459 </div>
460 </div>
460 % endif
461 % endif
461 </div>
462 </div>
462
463
463 % if not c.missing_commits:
464 % if not c.missing_commits:
464 % if c.compare_mode:
465 % if c.compare_mode:
465 % if c.at_version:
466 % if c.at_version:
466 <h4>
467 <h4>
467 ${_('Commits and changes between v{ver_from} and {ver_to} of this pull request, commenting is disabled').format(ver_from=c.from_version_pos, ver_to=c.at_version_pos if c.at_version_pos else 'latest')}:
468 ${_('Commits and changes between v{ver_from} and {ver_to} of this pull request, commenting is disabled').format(ver_from=c.from_version_pos, ver_to=c.at_version_pos if c.at_version_pos else 'latest')}:
468 </h4>
469 </h4>
469
470
470 <div class="subtitle-compare">
471 <div class="subtitle-compare">
471 ${_('commits added: {}, removed: {}').format(len(c.commit_changes_summary.added), len(c.commit_changes_summary.removed))}
472 ${_('commits added: {}, removed: {}').format(len(c.commit_changes_summary.added), len(c.commit_changes_summary.removed))}
472 </div>
473 </div>
473
474
474 <div class="container">
475 <div class="container">
475 <table class="rctable compare_view_commits">
476 <table class="rctable compare_view_commits">
476 <tr>
477 <tr>
477 <th></th>
478 <th></th>
478 <th>${_('Time')}</th>
479 <th>${_('Time')}</th>
479 <th>${_('Author')}</th>
480 <th>${_('Author')}</th>
480 <th>${_('Commit')}</th>
481 <th>${_('Commit')}</th>
481 <th></th>
482 <th></th>
482 <th>${_('Description')}</th>
483 <th>${_('Description')}</th>
483 </tr>
484 </tr>
484
485
485 % for c_type, commit in c.commit_changes:
486 % for c_type, commit in c.commit_changes:
486 % if c_type in ['a', 'r']:
487 % if c_type in ['a', 'r']:
487 <%
488 <%
488 if c_type == 'a':
489 if c_type == 'a':
489 cc_title = _('Commit added in displayed changes')
490 cc_title = _('Commit added in displayed changes')
490 elif c_type == 'r':
491 elif c_type == 'r':
491 cc_title = _('Commit removed in displayed changes')
492 cc_title = _('Commit removed in displayed changes')
492 else:
493 else:
493 cc_title = ''
494 cc_title = ''
494 %>
495 %>
495 <tr id="row-${commit.raw_id}" commit_id="${commit.raw_id}" class="compare_select">
496 <tr id="row-${commit.raw_id}" commit_id="${commit.raw_id}" class="compare_select">
496 <td>
497 <td>
497 <div class="commit-change-indicator color-${c_type}-border">
498 <div class="commit-change-indicator color-${c_type}-border">
498 <div class="commit-change-content color-${c_type} tooltip" title="${h.tooltip(cc_title)}">
499 <div class="commit-change-content color-${c_type} tooltip" title="${h.tooltip(cc_title)}">
499 ${c_type.upper()}
500 ${c_type.upper()}
500 </div>
501 </div>
501 </div>
502 </div>
502 </td>
503 </td>
503 <td class="td-time">
504 <td class="td-time">
504 ${h.age_component(commit.date)}
505 ${h.age_component(commit.date)}
505 </td>
506 </td>
506 <td class="td-user">
507 <td class="td-user">
507 ${base.gravatar_with_user(commit.author, 16)}
508 ${base.gravatar_with_user(commit.author, 16)}
508 </td>
509 </td>
509 <td class="td-hash">
510 <td class="td-hash">
510 <code>
511 <code>
511 <a href="${h.route_path('repo_commit', repo_name=c.target_repo.repo_name, commit_id=commit.raw_id)}">
512 <a href="${h.route_path('repo_commit', repo_name=c.target_repo.repo_name, commit_id=commit.raw_id)}">
512 r${commit.revision}:${h.short_id(commit.raw_id)}
513 r${commit.revision}:${h.short_id(commit.raw_id)}
513 </a>
514 </a>
514 ${h.hidden('revisions', commit.raw_id)}
515 ${h.hidden('revisions', commit.raw_id)}
515 </code>
516 </code>
516 </td>
517 </td>
517 <td class="expand_commit" data-commit-id="${commit.raw_id}" title="${_( 'Expand commit message')}">
518 <td class="expand_commit" data-commit-id="${commit.raw_id}" title="${_( 'Expand commit message')}">
518 <div class="show_more_col">
519 <div class="show_more_col">
519 <i class="show_more"></i>
520 <i class="show_more"></i>
520 </div>
521 </div>
521 </td>
522 </td>
522 <td class="mid td-description">
523 <td class="mid td-description">
523 <div class="log-container truncate-wrap">
524 <div class="log-container truncate-wrap">
524 <div class="message truncate" id="c-${commit.raw_id}" data-message-raw="${commit.message}">
525 <div class="message truncate" id="c-${commit.raw_id}" data-message-raw="${commit.message}">
525 ${h.urlify_commit_message(commit.message, c.repo_name)}
526 ${h.urlify_commit_message(commit.message, c.repo_name)}
526 </div>
527 </div>
527 </div>
528 </div>
528 </td>
529 </td>
529 </tr>
530 </tr>
530 % endif
531 % endif
531 % endfor
532 % endfor
532 </table>
533 </table>
533 </div>
534 </div>
534
535
535 <script>
536 <script>
536 $('.expand_commit').on('click',function(e){
537 $('.expand_commit').on('click',function(e){
537 var target_expand = $(this);
538 var target_expand = $(this);
538 var cid = target_expand.data('commitId');
539 var cid = target_expand.data('commitId');
539
540
540 if (target_expand.hasClass('open')){
541 if (target_expand.hasClass('open')){
541 $('#c-'+cid).css({
542 $('#c-'+cid).css({
542 'height': '1.5em',
543 'height': '1.5em',
543 'white-space': 'nowrap',
544 'white-space': 'nowrap',
544 'text-overflow': 'ellipsis',
545 'text-overflow': 'ellipsis',
545 'overflow':'hidden'
546 'overflow':'hidden'
546 });
547 });
547 target_expand.removeClass('open');
548 target_expand.removeClass('open');
548 }
549 }
549 else {
550 else {
550 $('#c-'+cid).css({
551 $('#c-'+cid).css({
551 'height': 'auto',
552 'height': 'auto',
552 'white-space': 'pre-line',
553 'white-space': 'pre-line',
553 'text-overflow': 'initial',
554 'text-overflow': 'initial',
554 'overflow':'visible'
555 'overflow':'visible'
555 });
556 });
556 target_expand.addClass('open');
557 target_expand.addClass('open');
557 }
558 }
558 });
559 });
559 </script>
560 </script>
560
561
561 % endif
562 % endif
562
563
563 % else:
564 % else:
564 <%include file="/compare/compare_commits.mako" />
565 <%include file="/compare/compare_commits.mako" />
565 % endif
566 % endif
566
567
567 <div class="cs_files">
568 <div class="cs_files">
568 <%namespace name="cbdiffs" file="/codeblocks/diffs.mako"/>
569 <%namespace name="cbdiffs" file="/codeblocks/diffs.mako"/>
569 ${cbdiffs.render_diffset_menu()}
570 ${cbdiffs.render_diffset_menu()}
570 ${cbdiffs.render_diffset(
571 ${cbdiffs.render_diffset(
571 c.diffset, use_comments=True,
572 c.diffset, use_comments=True,
572 collapse_when_files_over=30,
573 collapse_when_files_over=30,
573 disable_new_comments=not c.allowed_to_comment,
574 disable_new_comments=not c.allowed_to_comment,
574 deleted_files_comments=c.deleted_files_comments,
575 deleted_files_comments=c.deleted_files_comments,
575 inline_comments=c.inline_comments)}
576 inline_comments=c.inline_comments)}
576 </div>
577 </div>
577 % else:
578 % else:
578 ## skipping commits we need to clear the view for missing commits
579 ## skipping commits we need to clear the view for missing commits
579 <div style="clear:both;"></div>
580 <div style="clear:both;"></div>
580 % endif
581 % endif
581
582
582 </div>
583 </div>
583 </div>
584 </div>
584
585
585 ## template for inline comment form
586 ## template for inline comment form
586 <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
587 <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
587
588
588 ## render general comments
589 ## render general comments
589
590
590 <div id="comment-tr-show">
591 <div id="comment-tr-show">
591 <div class="comment">
592 <div class="comment">
592 % if general_outdated_comm_count_ver:
593 % if general_outdated_comm_count_ver:
593 <div class="meta">
594 <div class="meta">
594 % if general_outdated_comm_count_ver == 1:
595 % if general_outdated_comm_count_ver == 1:
595 ${_('there is {num} general comment from older versions').format(num=general_outdated_comm_count_ver)},
596 ${_('there is {num} general comment from older versions').format(num=general_outdated_comm_count_ver)},
596 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show it')}</a>
597 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show it')}</a>
597 % else:
598 % else:
598 ${_('there are {num} general comments from older versions').format(num=general_outdated_comm_count_ver)},
599 ${_('there are {num} general comments from older versions').format(num=general_outdated_comm_count_ver)},
599 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show them')}</a>
600 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show them')}</a>
600 % endif
601 % endif
601 </div>
602 </div>
602 % endif
603 % endif
603 </div>
604 </div>
604 </div>
605 </div>
605
606
606 ${comment.generate_comments(c.comments, include_pull_request=True, is_pull_request=True)}
607 ${comment.generate_comments(c.comments, include_pull_request=True, is_pull_request=True)}
607
608
608 % if not c.pull_request.is_closed():
609 % if not c.pull_request.is_closed():
609 ## merge status, and merge action
610 ## merge status, and merge action
610 <div class="pull-request-merge">
611 <div class="pull-request-merge">
611 <%include file="/pullrequests/pullrequest_merge_checks.mako"/>
612 <%include file="/pullrequests/pullrequest_merge_checks.mako"/>
612 </div>
613 </div>
613
614
614 ## main comment form and it status
615 ## main comment form and it status
615 ${comment.comments(h.route_path('pullrequest_comment_create', repo_name=c.repo_name,
616 ${comment.comments(h.route_path('pullrequest_comment_create', repo_name=c.repo_name,
616 pull_request_id=c.pull_request.pull_request_id),
617 pull_request_id=c.pull_request.pull_request_id),
617 c.pull_request_review_status,
618 c.pull_request_review_status,
618 is_pull_request=True, change_status=c.allowed_to_change_status)}
619 is_pull_request=True, change_status=c.allowed_to_change_status)}
619 %endif
620 %endif
620
621
621 <script type="text/javascript">
622 <script type="text/javascript">
622 if (location.hash) {
623 if (location.hash) {
623 var result = splitDelimitedHash(location.hash);
624 var result = splitDelimitedHash(location.hash);
624 var line = $('html').find(result.loc);
625 var line = $('html').find(result.loc);
625 // show hidden comments if we use location.hash
626 // show hidden comments if we use location.hash
626 if (line.hasClass('comment-general')) {
627 if (line.hasClass('comment-general')) {
627 $(line).show();
628 $(line).show();
628 } else if (line.hasClass('comment-inline')) {
629 } else if (line.hasClass('comment-inline')) {
629 $(line).show();
630 $(line).show();
630 var $cb = $(line).closest('.cb');
631 var $cb = $(line).closest('.cb');
631 $cb.removeClass('cb-collapsed')
632 $cb.removeClass('cb-collapsed')
632 }
633 }
633 if (line.length > 0){
634 if (line.length > 0){
634 offsetScroll(line, 70);
635 offsetScroll(line, 70);
635 }
636 }
636 }
637 }
637
638
638 versionController = new VersionController();
639 versionController = new VersionController();
639 versionController.init();
640 versionController.init();
640
641
641 reviewersController = new ReviewersController();
642 reviewersController = new ReviewersController();
642
643
643 $(function(){
644 $(function(){
644
645
645 // custom code mirror
646 // custom code mirror
646 var codeMirrorInstance = initPullRequestsCodeMirror('#pr-description-input');
647 var codeMirrorInstance = $('#pr-description-input').get(0).MarkupForm.cm;
647
648
648 var PRDetails = {
649 var PRDetails = {
649 editButton: $('#open_edit_pullrequest'),
650 editButton: $('#open_edit_pullrequest'),
650 closeButton: $('#close_edit_pullrequest'),
651 closeButton: $('#close_edit_pullrequest'),
651 deleteButton: $('#delete_pullrequest'),
652 deleteButton: $('#delete_pullrequest'),
652 viewFields: $('#pr-desc, #pr-title'),
653 viewFields: $('#pr-desc, #pr-title'),
653 editFields: $('#pr-desc-edit, #pr-title-edit, #pr-save'),
654 editFields: $('#pr-desc-edit, #pr-title-edit, #pr-save'),
654
655
655 init: function() {
656 init: function() {
656 var that = this;
657 var that = this;
657 this.editButton.on('click', function(e) { that.edit(); });
658 this.editButton.on('click', function(e) { that.edit(); });
658 this.closeButton.on('click', function(e) { that.view(); });
659 this.closeButton.on('click', function(e) { that.view(); });
659 },
660 },
660
661
661 edit: function(event) {
662 edit: function(event) {
662 this.viewFields.hide();
663 this.viewFields.hide();
663 this.editButton.hide();
664 this.editButton.hide();
664 this.deleteButton.hide();
665 this.deleteButton.hide();
665 this.closeButton.show();
666 this.closeButton.show();
666 this.editFields.show();
667 this.editFields.show();
667 codeMirrorInstance.refresh();
668 codeMirrorInstance.refresh();
668 },
669 },
669
670
670 view: function(event) {
671 view: function(event) {
671 this.editButton.show();
672 this.editButton.show();
672 this.deleteButton.show();
673 this.deleteButton.show();
673 this.editFields.hide();
674 this.editFields.hide();
674 this.closeButton.hide();
675 this.closeButton.hide();
675 this.viewFields.show();
676 this.viewFields.show();
676 }
677 }
677 };
678 };
678
679
679 var ReviewersPanel = {
680 var ReviewersPanel = {
680 editButton: $('#open_edit_reviewers'),
681 editButton: $('#open_edit_reviewers'),
681 closeButton: $('#close_edit_reviewers'),
682 closeButton: $('#close_edit_reviewers'),
682 addButton: $('#add_reviewer'),
683 addButton: $('#add_reviewer'),
683 removeButtons: $('.reviewer_member_remove,.reviewer_member_mandatory_remove'),
684 removeButtons: $('.reviewer_member_remove,.reviewer_member_mandatory_remove'),
684
685
685 init: function() {
686 init: function() {
686 var self = this;
687 var self = this;
687 this.editButton.on('click', function(e) { self.edit(); });
688 this.editButton.on('click', function(e) { self.edit(); });
688 this.closeButton.on('click', function(e) { self.close(); });
689 this.closeButton.on('click', function(e) { self.close(); });
689 },
690 },
690
691
691 edit: function(event) {
692 edit: function(event) {
692 this.editButton.hide();
693 this.editButton.hide();
693 this.closeButton.show();
694 this.closeButton.show();
694 this.addButton.show();
695 this.addButton.show();
695 this.removeButtons.css('visibility', 'visible');
696 this.removeButtons.css('visibility', 'visible');
696 // review rules
697 // review rules
697 reviewersController.loadReviewRules(
698 reviewersController.loadReviewRules(
698 ${c.pull_request.reviewer_data_json | n});
699 ${c.pull_request.reviewer_data_json | n});
699 },
700 },
700
701
701 close: function(event) {
702 close: function(event) {
702 this.editButton.show();
703 this.editButton.show();
703 this.closeButton.hide();
704 this.closeButton.hide();
704 this.addButton.hide();
705 this.addButton.hide();
705 this.removeButtons.css('visibility', 'hidden');
706 this.removeButtons.css('visibility', 'hidden');
706 // hide review rules
707 // hide review rules
707 reviewersController.hideReviewRules()
708 reviewersController.hideReviewRules()
708 }
709 }
709 };
710 };
710
711
711 PRDetails.init();
712 PRDetails.init();
712 ReviewersPanel.init();
713 ReviewersPanel.init();
713
714
714 showOutdated = function(self){
715 showOutdated = function(self){
715 $('.comment-inline.comment-outdated').show();
716 $('.comment-inline.comment-outdated').show();
716 $('.filediff-outdated').show();
717 $('.filediff-outdated').show();
717 $('.showOutdatedComments').hide();
718 $('.showOutdatedComments').hide();
718 $('.hideOutdatedComments').show();
719 $('.hideOutdatedComments').show();
719 };
720 };
720
721
721 hideOutdated = function(self){
722 hideOutdated = function(self){
722 $('.comment-inline.comment-outdated').hide();
723 $('.comment-inline.comment-outdated').hide();
723 $('.filediff-outdated').hide();
724 $('.filediff-outdated').hide();
724 $('.hideOutdatedComments').hide();
725 $('.hideOutdatedComments').hide();
725 $('.showOutdatedComments').show();
726 $('.showOutdatedComments').show();
726 };
727 };
727
728
728 refreshMergeChecks = function(){
729 refreshMergeChecks = function(){
729 var loadUrl = "${request.current_route_path(_query=dict(merge_checks=1))}";
730 var loadUrl = "${request.current_route_path(_query=dict(merge_checks=1))}";
730 $('.pull-request-merge').css('opacity', 0.3);
731 $('.pull-request-merge').css('opacity', 0.3);
731 $('.action-buttons-extra').css('opacity', 0.3);
732 $('.action-buttons-extra').css('opacity', 0.3);
732
733
733 $('.pull-request-merge').load(
734 $('.pull-request-merge').load(
734 loadUrl, function() {
735 loadUrl, function() {
735 $('.pull-request-merge').css('opacity', 1);
736 $('.pull-request-merge').css('opacity', 1);
736
737
737 $('.action-buttons-extra').css('opacity', 1);
738 $('.action-buttons-extra').css('opacity', 1);
738 injectCloseAction();
739 injectCloseAction();
739 }
740 }
740 );
741 );
741 };
742 };
742
743
743 injectCloseAction = function() {
744 injectCloseAction = function() {
744 var closeAction = $('#close-pull-request-action').html();
745 var closeAction = $('#close-pull-request-action').html();
745 var $actionButtons = $('.action-buttons-extra');
746 var $actionButtons = $('.action-buttons-extra');
746 // clear the action before
747 // clear the action before
747 $actionButtons.html("");
748 $actionButtons.html("");
748 $actionButtons.html(closeAction);
749 $actionButtons.html(closeAction);
749 };
750 };
750
751
751 closePullRequest = function (status) {
752 closePullRequest = function (status) {
752 // inject closing flag
753 // inject closing flag
753 $('.action-buttons-extra').append('<input type="hidden" class="close-pr-input" id="close_pull_request" value="1">');
754 $('.action-buttons-extra').append('<input type="hidden" class="close-pr-input" id="close_pull_request" value="1">');
754 $(generalCommentForm.statusChange).select2("val", status).trigger('change');
755 $(generalCommentForm.statusChange).select2("val", status).trigger('change');
755 $(generalCommentForm.submitForm).submit();
756 $(generalCommentForm.submitForm).submit();
756 };
757 };
757
758
758 $('#show-outdated-comments').on('click', function(e){
759 $('#show-outdated-comments').on('click', function(e){
759 var button = $(this);
760 var button = $(this);
760 var outdated = $('.comment-outdated');
761 var outdated = $('.comment-outdated');
761
762
762 if (button.html() === "(Show)") {
763 if (button.html() === "(Show)") {
763 button.html("(Hide)");
764 button.html("(Hide)");
764 outdated.show();
765 outdated.show();
765 } else {
766 } else {
766 button.html("(Show)");
767 button.html("(Show)");
767 outdated.hide();
768 outdated.hide();
768 }
769 }
769 });
770 });
770
771
771 $('.show-inline-comments').on('change', function(e){
772 $('.show-inline-comments').on('change', function(e){
772 var show = 'none';
773 var show = 'none';
773 var target = e.currentTarget;
774 var target = e.currentTarget;
774 if(target.checked){
775 if(target.checked){
775 show = ''
776 show = ''
776 }
777 }
777 var boxid = $(target).attr('id_for');
778 var boxid = $(target).attr('id_for');
778 var comments = $('#{0} .inline-comments'.format(boxid));
779 var comments = $('#{0} .inline-comments'.format(boxid));
779 var fn_display = function(idx){
780 var fn_display = function(idx){
780 $(this).css('display', show);
781 $(this).css('display', show);
781 };
782 };
782 $(comments).each(fn_display);
783 $(comments).each(fn_display);
783 var btns = $('#{0} .inline-comments-button'.format(boxid));
784 var btns = $('#{0} .inline-comments-button'.format(boxid));
784 $(btns).each(fn_display);
785 $(btns).each(fn_display);
785 });
786 });
786
787
787 $('#merge_pull_request_form').submit(function() {
788 $('#merge_pull_request_form').submit(function() {
788 if (!$('#merge_pull_request').attr('disabled')) {
789 if (!$('#merge_pull_request').attr('disabled')) {
789 $('#merge_pull_request').attr('disabled', 'disabled');
790 $('#merge_pull_request').attr('disabled', 'disabled');
790 }
791 }
791 return true;
792 return true;
792 });
793 });
793
794
794 $('#edit_pull_request').on('click', function(e){
795 $('#edit_pull_request').on('click', function(e){
795 var title = $('#pr-title-input').val();
796 var title = $('#pr-title-input').val();
796 var description = codeMirrorInstance.getValue();
797 var description = codeMirrorInstance.getValue();
797 editPullRequest(
798 editPullRequest(
798 "${c.repo_name}", "${c.pull_request.pull_request_id}",
799 "${c.repo_name}", "${c.pull_request.pull_request_id}",
799 title, description);
800 title, description);
800 });
801 });
801
802
802 $('#update_pull_request').on('click', function(e){
803 $('#update_pull_request').on('click', function(e){
803 $(this).attr('disabled', 'disabled');
804 $(this).attr('disabled', 'disabled');
804 $(this).addClass('disabled');
805 $(this).addClass('disabled');
805 $(this).html(_gettext('Saving...'));
806 $(this).html(_gettext('Saving...'));
806 reviewersController.updateReviewers(
807 reviewersController.updateReviewers(
807 "${c.repo_name}", "${c.pull_request.pull_request_id}");
808 "${c.repo_name}", "${c.pull_request.pull_request_id}");
808 });
809 });
809
810
810 $('#update_commits').on('click', function(e){
811 $('#update_commits').on('click', function(e){
811 var isDisabled = !$(e.currentTarget).attr('disabled');
812 var isDisabled = !$(e.currentTarget).attr('disabled');
812 $(e.currentTarget).attr('disabled', 'disabled');
813 $(e.currentTarget).attr('disabled', 'disabled');
813 $(e.currentTarget).addClass('disabled');
814 $(e.currentTarget).addClass('disabled');
814 $(e.currentTarget).removeClass('btn-primary');
815 $(e.currentTarget).removeClass('btn-primary');
815 $(e.currentTarget).text(_gettext('Updating...'));
816 $(e.currentTarget).text(_gettext('Updating...'));
816 if(isDisabled){
817 if(isDisabled){
817 updateCommits(
818 updateCommits(
818 "${c.repo_name}", "${c.pull_request.pull_request_id}");
819 "${c.repo_name}", "${c.pull_request.pull_request_id}");
819 }
820 }
820 });
821 });
821 // fixing issue with caches on firefox
822 // fixing issue with caches on firefox
822 $('#update_commits').removeAttr("disabled");
823 $('#update_commits').removeAttr("disabled");
823
824
824 $('.show-inline-comments').on('click', function(e){
825 $('.show-inline-comments').on('click', function(e){
825 var boxid = $(this).attr('data-comment-id');
826 var boxid = $(this).attr('data-comment-id');
826 var button = $(this);
827 var button = $(this);
827
828
828 if(button.hasClass("comments-visible")) {
829 if(button.hasClass("comments-visible")) {
829 $('#{0} .inline-comments'.format(boxid)).each(function(index){
830 $('#{0} .inline-comments'.format(boxid)).each(function(index){
830 $(this).hide();
831 $(this).hide();
831 });
832 });
832 button.removeClass("comments-visible");
833 button.removeClass("comments-visible");
833 } else {
834 } else {
834 $('#{0} .inline-comments'.format(boxid)).each(function(index){
835 $('#{0} .inline-comments'.format(boxid)).each(function(index){
835 $(this).show();
836 $(this).show();
836 });
837 });
837 button.addClass("comments-visible");
838 button.addClass("comments-visible");
838 }
839 }
839 });
840 });
840
841
841 // register submit callback on commentForm form to track TODOs
842 // register submit callback on commentForm form to track TODOs
842 window.commentFormGlobalSubmitSuccessCallback = function(){
843 window.commentFormGlobalSubmitSuccessCallback = function(){
843 refreshMergeChecks();
844 refreshMergeChecks();
844 };
845 };
845 // initial injection
846 // initial injection
846 injectCloseAction();
847 injectCloseAction();
847
848
848 ReviewerAutoComplete('#user');
849 ReviewerAutoComplete('#user');
849
850
850 })
851 })
851 </script>
852 </script>
852
853
853 </div>
854 </div>
854 </div>
855 </div>
855
856
856 </%def>
857 </%def>
General Comments 0
You need to be logged in to leave comments. Login now