##// END OF EJS Templates
pull-requests: make my account and repo pr table more consistent.
marcink -
r4512:91239784 stable
parent child Browse files
Show More
@@ -1,1811 +1,1812 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2020 RhodeCode GmbH
3 # Copyright (C) 2011-2020 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 logging
21 import logging
22 import collections
22 import collections
23
23
24 import formencode
24 import formencode
25 import formencode.htmlfill
25 import formencode.htmlfill
26 import peppercorn
26 import peppercorn
27 from pyramid.httpexceptions import (
27 from pyramid.httpexceptions import (
28 HTTPFound, HTTPNotFound, HTTPForbidden, HTTPBadRequest, HTTPConflict)
28 HTTPFound, HTTPNotFound, HTTPForbidden, HTTPBadRequest, HTTPConflict)
29 from pyramid.view import view_config
29 from pyramid.view import view_config
30 from pyramid.renderers import render
30 from pyramid.renderers import render
31
31
32 from rhodecode.apps._base import RepoAppView, DataGridAppView
32 from rhodecode.apps._base import RepoAppView, DataGridAppView
33
33
34 from rhodecode.lib import helpers as h, diffs, codeblocks, channelstream
34 from rhodecode.lib import helpers as h, diffs, codeblocks, channelstream
35 from rhodecode.lib.base import vcs_operation_context
35 from rhodecode.lib.base import vcs_operation_context
36 from rhodecode.lib.diffs import load_cached_diff, cache_diff, diff_cache_exist
36 from rhodecode.lib.diffs import load_cached_diff, cache_diff, diff_cache_exist
37 from rhodecode.lib.exceptions import CommentVersionMismatch
37 from rhodecode.lib.exceptions import CommentVersionMismatch
38 from rhodecode.lib.ext_json import json
38 from rhodecode.lib.ext_json import json
39 from rhodecode.lib.auth import (
39 from rhodecode.lib.auth import (
40 LoginRequired, HasRepoPermissionAny, HasRepoPermissionAnyDecorator,
40 LoginRequired, HasRepoPermissionAny, HasRepoPermissionAnyDecorator,
41 NotAnonymous, CSRFRequired)
41 NotAnonymous, CSRFRequired)
42 from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode, safe_int, aslist
42 from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode, safe_int, aslist
43 from rhodecode.lib.vcs.backends.base import EmptyCommit, UpdateFailureReason, Reference
43 from rhodecode.lib.vcs.backends.base import EmptyCommit, UpdateFailureReason, Reference
44 from rhodecode.lib.vcs.exceptions import (
44 from rhodecode.lib.vcs.exceptions import (
45 CommitDoesNotExistError, RepositoryRequirementError, EmptyRepositoryError)
45 CommitDoesNotExistError, RepositoryRequirementError, EmptyRepositoryError)
46 from rhodecode.model.changeset_status import ChangesetStatusModel
46 from rhodecode.model.changeset_status import ChangesetStatusModel
47 from rhodecode.model.comment import CommentsModel
47 from rhodecode.model.comment import CommentsModel
48 from rhodecode.model.db import (
48 from rhodecode.model.db import (
49 func, or_, PullRequest, ChangesetComment, ChangesetStatus, Repository,
49 func, or_, PullRequest, ChangesetComment, ChangesetStatus, Repository,
50 PullRequestReviewers)
50 PullRequestReviewers)
51 from rhodecode.model.forms import PullRequestForm
51 from rhodecode.model.forms import PullRequestForm
52 from rhodecode.model.meta import Session
52 from rhodecode.model.meta import Session
53 from rhodecode.model.pull_request import PullRequestModel, MergeCheck
53 from rhodecode.model.pull_request import PullRequestModel, MergeCheck
54 from rhodecode.model.scm import ScmModel
54 from rhodecode.model.scm import ScmModel
55
55
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58
58
59 class RepoPullRequestsView(RepoAppView, DataGridAppView):
59 class RepoPullRequestsView(RepoAppView, DataGridAppView):
60
60
61 def load_default_context(self):
61 def load_default_context(self):
62 c = self._get_local_tmpl_context(include_app_defaults=True)
62 c = self._get_local_tmpl_context(include_app_defaults=True)
63 c.REVIEW_STATUS_APPROVED = ChangesetStatus.STATUS_APPROVED
63 c.REVIEW_STATUS_APPROVED = ChangesetStatus.STATUS_APPROVED
64 c.REVIEW_STATUS_REJECTED = ChangesetStatus.STATUS_REJECTED
64 c.REVIEW_STATUS_REJECTED = ChangesetStatus.STATUS_REJECTED
65 # backward compat., we use for OLD PRs a plain renderer
65 # backward compat., we use for OLD PRs a plain renderer
66 c.renderer = 'plain'
66 c.renderer = 'plain'
67 return c
67 return c
68
68
69 def _get_pull_requests_list(
69 def _get_pull_requests_list(
70 self, repo_name, source, filter_type, opened_by, statuses):
70 self, repo_name, source, filter_type, opened_by, statuses):
71
71
72 draw, start, limit = self._extract_chunk(self.request)
72 draw, start, limit = self._extract_chunk(self.request)
73 search_q, order_by, order_dir = self._extract_ordering(self.request)
73 search_q, order_by, order_dir = self._extract_ordering(self.request)
74 _render = self.request.get_partial_renderer(
74 _render = self.request.get_partial_renderer(
75 'rhodecode:templates/data_table/_dt_elements.mako')
75 'rhodecode:templates/data_table/_dt_elements.mako')
76
76
77 # pagination
77 # pagination
78
78
79 if filter_type == 'awaiting_review':
79 if filter_type == 'awaiting_review':
80 pull_requests = PullRequestModel().get_awaiting_review(
80 pull_requests = PullRequestModel().get_awaiting_review(
81 repo_name, search_q=search_q, source=source, opened_by=opened_by,
81 repo_name, search_q=search_q, source=source, opened_by=opened_by,
82 statuses=statuses, offset=start, length=limit,
82 statuses=statuses, offset=start, length=limit,
83 order_by=order_by, order_dir=order_dir)
83 order_by=order_by, order_dir=order_dir)
84 pull_requests_total_count = PullRequestModel().count_awaiting_review(
84 pull_requests_total_count = PullRequestModel().count_awaiting_review(
85 repo_name, search_q=search_q, source=source, statuses=statuses,
85 repo_name, search_q=search_q, source=source, statuses=statuses,
86 opened_by=opened_by)
86 opened_by=opened_by)
87 elif filter_type == 'awaiting_my_review':
87 elif filter_type == 'awaiting_my_review':
88 pull_requests = PullRequestModel().get_awaiting_my_review(
88 pull_requests = PullRequestModel().get_awaiting_my_review(
89 repo_name, search_q=search_q, source=source, opened_by=opened_by,
89 repo_name, search_q=search_q, source=source, opened_by=opened_by,
90 user_id=self._rhodecode_user.user_id, statuses=statuses,
90 user_id=self._rhodecode_user.user_id, statuses=statuses,
91 offset=start, length=limit, order_by=order_by,
91 offset=start, length=limit, order_by=order_by,
92 order_dir=order_dir)
92 order_dir=order_dir)
93 pull_requests_total_count = PullRequestModel().count_awaiting_my_review(
93 pull_requests_total_count = PullRequestModel().count_awaiting_my_review(
94 repo_name, search_q=search_q, source=source, user_id=self._rhodecode_user.user_id,
94 repo_name, search_q=search_q, source=source, user_id=self._rhodecode_user.user_id,
95 statuses=statuses, opened_by=opened_by)
95 statuses=statuses, opened_by=opened_by)
96 else:
96 else:
97 pull_requests = PullRequestModel().get_all(
97 pull_requests = PullRequestModel().get_all(
98 repo_name, search_q=search_q, source=source, opened_by=opened_by,
98 repo_name, search_q=search_q, source=source, opened_by=opened_by,
99 statuses=statuses, offset=start, length=limit,
99 statuses=statuses, offset=start, length=limit,
100 order_by=order_by, order_dir=order_dir)
100 order_by=order_by, order_dir=order_dir)
101 pull_requests_total_count = PullRequestModel().count_all(
101 pull_requests_total_count = PullRequestModel().count_all(
102 repo_name, search_q=search_q, source=source, statuses=statuses,
102 repo_name, search_q=search_q, source=source, statuses=statuses,
103 opened_by=opened_by)
103 opened_by=opened_by)
104
104
105 data = []
105 data = []
106 comments_model = CommentsModel()
106 comments_model = CommentsModel()
107 for pr in pull_requests:
107 for pr in pull_requests:
108 comments_count = comments_model.get_all_comments(
108 comments_count = comments_model.get_all_comments(
109 self.db_repo.repo_id, pull_request=pr, count_only=True)
109 self.db_repo.repo_id, pull_request=pr, count_only=True)
110
110
111 data.append({
111 data.append({
112 'name': _render('pullrequest_name',
112 'name': _render('pullrequest_name',
113 pr.pull_request_id, pr.pull_request_state,
113 pr.pull_request_id, pr.pull_request_state,
114 pr.work_in_progress, pr.target_repo.repo_name),
114 pr.work_in_progress, pr.target_repo.repo_name,
115 short=True),
115 'name_raw': pr.pull_request_id,
116 'name_raw': pr.pull_request_id,
116 'status': _render('pullrequest_status',
117 'status': _render('pullrequest_status',
117 pr.calculated_review_status()),
118 pr.calculated_review_status()),
118 'title': _render('pullrequest_title', pr.title, pr.description),
119 'title': _render('pullrequest_title', pr.title, pr.description),
119 'description': h.escape(pr.description),
120 'description': h.escape(pr.description),
120 'updated_on': _render('pullrequest_updated_on',
121 'updated_on': _render('pullrequest_updated_on',
121 h.datetime_to_time(pr.updated_on)),
122 h.datetime_to_time(pr.updated_on)),
122 'updated_on_raw': h.datetime_to_time(pr.updated_on),
123 'updated_on_raw': h.datetime_to_time(pr.updated_on),
123 'created_on': _render('pullrequest_updated_on',
124 'created_on': _render('pullrequest_updated_on',
124 h.datetime_to_time(pr.created_on)),
125 h.datetime_to_time(pr.created_on)),
125 'created_on_raw': h.datetime_to_time(pr.created_on),
126 'created_on_raw': h.datetime_to_time(pr.created_on),
126 'state': pr.pull_request_state,
127 'state': pr.pull_request_state,
127 'author': _render('pullrequest_author',
128 'author': _render('pullrequest_author',
128 pr.author.full_contact, ),
129 pr.author.full_contact, ),
129 'author_raw': pr.author.full_name,
130 'author_raw': pr.author.full_name,
130 'comments': _render('pullrequest_comments', comments_count),
131 'comments': _render('pullrequest_comments', comments_count),
131 'comments_raw': comments_count,
132 'comments_raw': comments_count,
132 'closed': pr.is_closed(),
133 'closed': pr.is_closed(),
133 })
134 })
134
135
135 data = ({
136 data = ({
136 'draw': draw,
137 'draw': draw,
137 'data': data,
138 'data': data,
138 'recordsTotal': pull_requests_total_count,
139 'recordsTotal': pull_requests_total_count,
139 'recordsFiltered': pull_requests_total_count,
140 'recordsFiltered': pull_requests_total_count,
140 })
141 })
141 return data
142 return data
142
143
143 @LoginRequired()
144 @LoginRequired()
144 @HasRepoPermissionAnyDecorator(
145 @HasRepoPermissionAnyDecorator(
145 'repository.read', 'repository.write', 'repository.admin')
146 'repository.read', 'repository.write', 'repository.admin')
146 @view_config(
147 @view_config(
147 route_name='pullrequest_show_all', request_method='GET',
148 route_name='pullrequest_show_all', request_method='GET',
148 renderer='rhodecode:templates/pullrequests/pullrequests.mako')
149 renderer='rhodecode:templates/pullrequests/pullrequests.mako')
149 def pull_request_list(self):
150 def pull_request_list(self):
150 c = self.load_default_context()
151 c = self.load_default_context()
151
152
152 req_get = self.request.GET
153 req_get = self.request.GET
153 c.source = str2bool(req_get.get('source'))
154 c.source = str2bool(req_get.get('source'))
154 c.closed = str2bool(req_get.get('closed'))
155 c.closed = str2bool(req_get.get('closed'))
155 c.my = str2bool(req_get.get('my'))
156 c.my = str2bool(req_get.get('my'))
156 c.awaiting_review = str2bool(req_get.get('awaiting_review'))
157 c.awaiting_review = str2bool(req_get.get('awaiting_review'))
157 c.awaiting_my_review = str2bool(req_get.get('awaiting_my_review'))
158 c.awaiting_my_review = str2bool(req_get.get('awaiting_my_review'))
158
159
159 c.active = 'open'
160 c.active = 'open'
160 if c.my:
161 if c.my:
161 c.active = 'my'
162 c.active = 'my'
162 if c.closed:
163 if c.closed:
163 c.active = 'closed'
164 c.active = 'closed'
164 if c.awaiting_review and not c.source:
165 if c.awaiting_review and not c.source:
165 c.active = 'awaiting'
166 c.active = 'awaiting'
166 if c.source and not c.awaiting_review:
167 if c.source and not c.awaiting_review:
167 c.active = 'source'
168 c.active = 'source'
168 if c.awaiting_my_review:
169 if c.awaiting_my_review:
169 c.active = 'awaiting_my'
170 c.active = 'awaiting_my'
170
171
171 return self._get_template_context(c)
172 return self._get_template_context(c)
172
173
173 @LoginRequired()
174 @LoginRequired()
174 @HasRepoPermissionAnyDecorator(
175 @HasRepoPermissionAnyDecorator(
175 'repository.read', 'repository.write', 'repository.admin')
176 'repository.read', 'repository.write', 'repository.admin')
176 @view_config(
177 @view_config(
177 route_name='pullrequest_show_all_data', request_method='GET',
178 route_name='pullrequest_show_all_data', request_method='GET',
178 renderer='json_ext', xhr=True)
179 renderer='json_ext', xhr=True)
179 def pull_request_list_data(self):
180 def pull_request_list_data(self):
180 self.load_default_context()
181 self.load_default_context()
181
182
182 # additional filters
183 # additional filters
183 req_get = self.request.GET
184 req_get = self.request.GET
184 source = str2bool(req_get.get('source'))
185 source = str2bool(req_get.get('source'))
185 closed = str2bool(req_get.get('closed'))
186 closed = str2bool(req_get.get('closed'))
186 my = str2bool(req_get.get('my'))
187 my = str2bool(req_get.get('my'))
187 awaiting_review = str2bool(req_get.get('awaiting_review'))
188 awaiting_review = str2bool(req_get.get('awaiting_review'))
188 awaiting_my_review = str2bool(req_get.get('awaiting_my_review'))
189 awaiting_my_review = str2bool(req_get.get('awaiting_my_review'))
189
190
190 filter_type = 'awaiting_review' if awaiting_review \
191 filter_type = 'awaiting_review' if awaiting_review \
191 else 'awaiting_my_review' if awaiting_my_review \
192 else 'awaiting_my_review' if awaiting_my_review \
192 else None
193 else None
193
194
194 opened_by = None
195 opened_by = None
195 if my:
196 if my:
196 opened_by = [self._rhodecode_user.user_id]
197 opened_by = [self._rhodecode_user.user_id]
197
198
198 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
199 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
199 if closed:
200 if closed:
200 statuses = [PullRequest.STATUS_CLOSED]
201 statuses = [PullRequest.STATUS_CLOSED]
201
202
202 data = self._get_pull_requests_list(
203 data = self._get_pull_requests_list(
203 repo_name=self.db_repo_name, source=source,
204 repo_name=self.db_repo_name, source=source,
204 filter_type=filter_type, opened_by=opened_by, statuses=statuses)
205 filter_type=filter_type, opened_by=opened_by, statuses=statuses)
205
206
206 return data
207 return data
207
208
208 def _is_diff_cache_enabled(self, target_repo):
209 def _is_diff_cache_enabled(self, target_repo):
209 caching_enabled = self._get_general_setting(
210 caching_enabled = self._get_general_setting(
210 target_repo, 'rhodecode_diff_cache')
211 target_repo, 'rhodecode_diff_cache')
211 log.debug('Diff caching enabled: %s', caching_enabled)
212 log.debug('Diff caching enabled: %s', caching_enabled)
212 return caching_enabled
213 return caching_enabled
213
214
214 def _get_diffset(self, source_repo_name, source_repo,
215 def _get_diffset(self, source_repo_name, source_repo,
215 ancestor_commit,
216 ancestor_commit,
216 source_ref_id, target_ref_id,
217 source_ref_id, target_ref_id,
217 target_commit, source_commit, diff_limit, file_limit,
218 target_commit, source_commit, diff_limit, file_limit,
218 fulldiff, hide_whitespace_changes, diff_context, use_ancestor=True):
219 fulldiff, hide_whitespace_changes, diff_context, use_ancestor=True):
219
220
220 if use_ancestor:
221 if use_ancestor:
221 # we might want to not use it for versions
222 # we might want to not use it for versions
222 target_ref_id = ancestor_commit.raw_id
223 target_ref_id = ancestor_commit.raw_id
223
224
224 vcs_diff = PullRequestModel().get_diff(
225 vcs_diff = PullRequestModel().get_diff(
225 source_repo, source_ref_id, target_ref_id,
226 source_repo, source_ref_id, target_ref_id,
226 hide_whitespace_changes, diff_context)
227 hide_whitespace_changes, diff_context)
227
228
228 diff_processor = diffs.DiffProcessor(
229 diff_processor = diffs.DiffProcessor(
229 vcs_diff, format='newdiff', diff_limit=diff_limit,
230 vcs_diff, format='newdiff', diff_limit=diff_limit,
230 file_limit=file_limit, show_full_diff=fulldiff)
231 file_limit=file_limit, show_full_diff=fulldiff)
231
232
232 _parsed = diff_processor.prepare()
233 _parsed = diff_processor.prepare()
233
234
234 diffset = codeblocks.DiffSet(
235 diffset = codeblocks.DiffSet(
235 repo_name=self.db_repo_name,
236 repo_name=self.db_repo_name,
236 source_repo_name=source_repo_name,
237 source_repo_name=source_repo_name,
237 source_node_getter=codeblocks.diffset_node_getter(target_commit),
238 source_node_getter=codeblocks.diffset_node_getter(target_commit),
238 target_node_getter=codeblocks.diffset_node_getter(source_commit),
239 target_node_getter=codeblocks.diffset_node_getter(source_commit),
239 )
240 )
240 diffset = self.path_filter.render_patchset_filtered(
241 diffset = self.path_filter.render_patchset_filtered(
241 diffset, _parsed, target_commit.raw_id, source_commit.raw_id)
242 diffset, _parsed, target_commit.raw_id, source_commit.raw_id)
242
243
243 return diffset
244 return diffset
244
245
245 def _get_range_diffset(self, source_scm, source_repo,
246 def _get_range_diffset(self, source_scm, source_repo,
246 commit1, commit2, diff_limit, file_limit,
247 commit1, commit2, diff_limit, file_limit,
247 fulldiff, hide_whitespace_changes, diff_context):
248 fulldiff, hide_whitespace_changes, diff_context):
248 vcs_diff = source_scm.get_diff(
249 vcs_diff = source_scm.get_diff(
249 commit1, commit2,
250 commit1, commit2,
250 ignore_whitespace=hide_whitespace_changes,
251 ignore_whitespace=hide_whitespace_changes,
251 context=diff_context)
252 context=diff_context)
252
253
253 diff_processor = diffs.DiffProcessor(
254 diff_processor = diffs.DiffProcessor(
254 vcs_diff, format='newdiff', diff_limit=diff_limit,
255 vcs_diff, format='newdiff', diff_limit=diff_limit,
255 file_limit=file_limit, show_full_diff=fulldiff)
256 file_limit=file_limit, show_full_diff=fulldiff)
256
257
257 _parsed = diff_processor.prepare()
258 _parsed = diff_processor.prepare()
258
259
259 diffset = codeblocks.DiffSet(
260 diffset = codeblocks.DiffSet(
260 repo_name=source_repo.repo_name,
261 repo_name=source_repo.repo_name,
261 source_node_getter=codeblocks.diffset_node_getter(commit1),
262 source_node_getter=codeblocks.diffset_node_getter(commit1),
262 target_node_getter=codeblocks.diffset_node_getter(commit2))
263 target_node_getter=codeblocks.diffset_node_getter(commit2))
263
264
264 diffset = self.path_filter.render_patchset_filtered(
265 diffset = self.path_filter.render_patchset_filtered(
265 diffset, _parsed, commit1.raw_id, commit2.raw_id)
266 diffset, _parsed, commit1.raw_id, commit2.raw_id)
266
267
267 return diffset
268 return diffset
268
269
269 def register_comments_vars(self, c, pull_request, versions):
270 def register_comments_vars(self, c, pull_request, versions):
270 comments_model = CommentsModel()
271 comments_model = CommentsModel()
271
272
272 # GENERAL COMMENTS with versions #
273 # GENERAL COMMENTS with versions #
273 q = comments_model._all_general_comments_of_pull_request(pull_request)
274 q = comments_model._all_general_comments_of_pull_request(pull_request)
274 q = q.order_by(ChangesetComment.comment_id.asc())
275 q = q.order_by(ChangesetComment.comment_id.asc())
275 general_comments = q
276 general_comments = q
276
277
277 # pick comments we want to render at current version
278 # pick comments we want to render at current version
278 c.comment_versions = comments_model.aggregate_comments(
279 c.comment_versions = comments_model.aggregate_comments(
279 general_comments, versions, c.at_version_num)
280 general_comments, versions, c.at_version_num)
280
281
281 # INLINE COMMENTS with versions #
282 # INLINE COMMENTS with versions #
282 q = comments_model._all_inline_comments_of_pull_request(pull_request)
283 q = comments_model._all_inline_comments_of_pull_request(pull_request)
283 q = q.order_by(ChangesetComment.comment_id.asc())
284 q = q.order_by(ChangesetComment.comment_id.asc())
284 inline_comments = q
285 inline_comments = q
285
286
286 c.inline_versions = comments_model.aggregate_comments(
287 c.inline_versions = comments_model.aggregate_comments(
287 inline_comments, versions, c.at_version_num, inline=True)
288 inline_comments, versions, c.at_version_num, inline=True)
288
289
289 # Comments inline+general
290 # Comments inline+general
290 if c.at_version:
291 if c.at_version:
291 c.inline_comments_flat = c.inline_versions[c.at_version_num]['display']
292 c.inline_comments_flat = c.inline_versions[c.at_version_num]['display']
292 c.comments = c.comment_versions[c.at_version_num]['display']
293 c.comments = c.comment_versions[c.at_version_num]['display']
293 else:
294 else:
294 c.inline_comments_flat = c.inline_versions[c.at_version_num]['until']
295 c.inline_comments_flat = c.inline_versions[c.at_version_num]['until']
295 c.comments = c.comment_versions[c.at_version_num]['until']
296 c.comments = c.comment_versions[c.at_version_num]['until']
296
297
297 return general_comments, inline_comments
298 return general_comments, inline_comments
298
299
299 @LoginRequired()
300 @LoginRequired()
300 @HasRepoPermissionAnyDecorator(
301 @HasRepoPermissionAnyDecorator(
301 'repository.read', 'repository.write', 'repository.admin')
302 'repository.read', 'repository.write', 'repository.admin')
302 @view_config(
303 @view_config(
303 route_name='pullrequest_show', request_method='GET',
304 route_name='pullrequest_show', request_method='GET',
304 renderer='rhodecode:templates/pullrequests/pullrequest_show.mako')
305 renderer='rhodecode:templates/pullrequests/pullrequest_show.mako')
305 def pull_request_show(self):
306 def pull_request_show(self):
306 _ = self.request.translate
307 _ = self.request.translate
307 c = self.load_default_context()
308 c = self.load_default_context()
308
309
309 pull_request = PullRequest.get_or_404(
310 pull_request = PullRequest.get_or_404(
310 self.request.matchdict['pull_request_id'])
311 self.request.matchdict['pull_request_id'])
311 pull_request_id = pull_request.pull_request_id
312 pull_request_id = pull_request.pull_request_id
312
313
313 c.state_progressing = pull_request.is_state_changing()
314 c.state_progressing = pull_request.is_state_changing()
314 c.pr_broadcast_channel = channelstream.pr_channel(pull_request)
315 c.pr_broadcast_channel = channelstream.pr_channel(pull_request)
315
316
316 _new_state = {
317 _new_state = {
317 'created': PullRequest.STATE_CREATED,
318 'created': PullRequest.STATE_CREATED,
318 }.get(self.request.GET.get('force_state'))
319 }.get(self.request.GET.get('force_state'))
319
320
320 if c.is_super_admin and _new_state:
321 if c.is_super_admin and _new_state:
321 with pull_request.set_state(PullRequest.STATE_UPDATING, final_state=_new_state):
322 with pull_request.set_state(PullRequest.STATE_UPDATING, final_state=_new_state):
322 h.flash(
323 h.flash(
323 _('Pull Request state was force changed to `{}`').format(_new_state),
324 _('Pull Request state was force changed to `{}`').format(_new_state),
324 category='success')
325 category='success')
325 Session().commit()
326 Session().commit()
326
327
327 raise HTTPFound(h.route_path(
328 raise HTTPFound(h.route_path(
328 'pullrequest_show', repo_name=self.db_repo_name,
329 'pullrequest_show', repo_name=self.db_repo_name,
329 pull_request_id=pull_request_id))
330 pull_request_id=pull_request_id))
330
331
331 version = self.request.GET.get('version')
332 version = self.request.GET.get('version')
332 from_version = self.request.GET.get('from_version') or version
333 from_version = self.request.GET.get('from_version') or version
333 merge_checks = self.request.GET.get('merge_checks')
334 merge_checks = self.request.GET.get('merge_checks')
334 c.fulldiff = str2bool(self.request.GET.get('fulldiff'))
335 c.fulldiff = str2bool(self.request.GET.get('fulldiff'))
335 force_refresh = str2bool(self.request.GET.get('force_refresh'))
336 force_refresh = str2bool(self.request.GET.get('force_refresh'))
336 c.range_diff_on = self.request.GET.get('range-diff') == "1"
337 c.range_diff_on = self.request.GET.get('range-diff') == "1"
337
338
338 # fetch global flags of ignore ws or context lines
339 # fetch global flags of ignore ws or context lines
339 diff_context = diffs.get_diff_context(self.request)
340 diff_context = diffs.get_diff_context(self.request)
340 hide_whitespace_changes = diffs.get_diff_whitespace_flag(self.request)
341 hide_whitespace_changes = diffs.get_diff_whitespace_flag(self.request)
341
342
342 (pull_request_latest,
343 (pull_request_latest,
343 pull_request_at_ver,
344 pull_request_at_ver,
344 pull_request_display_obj,
345 pull_request_display_obj,
345 at_version) = PullRequestModel().get_pr_version(
346 at_version) = PullRequestModel().get_pr_version(
346 pull_request_id, version=version)
347 pull_request_id, version=version)
347
348
348 pr_closed = pull_request_latest.is_closed()
349 pr_closed = pull_request_latest.is_closed()
349
350
350 if pr_closed and (version or from_version):
351 if pr_closed and (version or from_version):
351 # not allow to browse versions for closed PR
352 # not allow to browse versions for closed PR
352 raise HTTPFound(h.route_path(
353 raise HTTPFound(h.route_path(
353 'pullrequest_show', repo_name=self.db_repo_name,
354 'pullrequest_show', repo_name=self.db_repo_name,
354 pull_request_id=pull_request_id))
355 pull_request_id=pull_request_id))
355
356
356 versions = pull_request_display_obj.versions()
357 versions = pull_request_display_obj.versions()
357 # used to store per-commit range diffs
358 # used to store per-commit range diffs
358 c.changes = collections.OrderedDict()
359 c.changes = collections.OrderedDict()
359
360
360 c.at_version = at_version
361 c.at_version = at_version
361 c.at_version_num = (at_version
362 c.at_version_num = (at_version
362 if at_version and at_version != PullRequest.LATEST_VER
363 if at_version and at_version != PullRequest.LATEST_VER
363 else None)
364 else None)
364
365
365 c.at_version_index = ChangesetComment.get_index_from_version(
366 c.at_version_index = ChangesetComment.get_index_from_version(
366 c.at_version_num, versions)
367 c.at_version_num, versions)
367
368
368 (prev_pull_request_latest,
369 (prev_pull_request_latest,
369 prev_pull_request_at_ver,
370 prev_pull_request_at_ver,
370 prev_pull_request_display_obj,
371 prev_pull_request_display_obj,
371 prev_at_version) = PullRequestModel().get_pr_version(
372 prev_at_version) = PullRequestModel().get_pr_version(
372 pull_request_id, version=from_version)
373 pull_request_id, version=from_version)
373
374
374 c.from_version = prev_at_version
375 c.from_version = prev_at_version
375 c.from_version_num = (prev_at_version
376 c.from_version_num = (prev_at_version
376 if prev_at_version and prev_at_version != PullRequest.LATEST_VER
377 if prev_at_version and prev_at_version != PullRequest.LATEST_VER
377 else None)
378 else None)
378 c.from_version_index = ChangesetComment.get_index_from_version(
379 c.from_version_index = ChangesetComment.get_index_from_version(
379 c.from_version_num, versions)
380 c.from_version_num, versions)
380
381
381 # define if we're in COMPARE mode or VIEW at version mode
382 # define if we're in COMPARE mode or VIEW at version mode
382 compare = at_version != prev_at_version
383 compare = at_version != prev_at_version
383
384
384 # pull_requests repo_name we opened it against
385 # pull_requests repo_name we opened it against
385 # ie. target_repo must match
386 # ie. target_repo must match
386 if self.db_repo_name != pull_request_at_ver.target_repo.repo_name:
387 if self.db_repo_name != pull_request_at_ver.target_repo.repo_name:
387 log.warning('Mismatch between the current repo: %s, and target %s',
388 log.warning('Mismatch between the current repo: %s, and target %s',
388 self.db_repo_name, pull_request_at_ver.target_repo.repo_name)
389 self.db_repo_name, pull_request_at_ver.target_repo.repo_name)
389 raise HTTPNotFound()
390 raise HTTPNotFound()
390
391
391 c.shadow_clone_url = PullRequestModel().get_shadow_clone_url(pull_request_at_ver)
392 c.shadow_clone_url = PullRequestModel().get_shadow_clone_url(pull_request_at_ver)
392
393
393 c.pull_request = pull_request_display_obj
394 c.pull_request = pull_request_display_obj
394 c.renderer = pull_request_at_ver.description_renderer or c.renderer
395 c.renderer = pull_request_at_ver.description_renderer or c.renderer
395 c.pull_request_latest = pull_request_latest
396 c.pull_request_latest = pull_request_latest
396
397
397 # inject latest version
398 # inject latest version
398 latest_ver = PullRequest.get_pr_display_object(pull_request_latest, pull_request_latest)
399 latest_ver = PullRequest.get_pr_display_object(pull_request_latest, pull_request_latest)
399 c.versions = versions + [latest_ver]
400 c.versions = versions + [latest_ver]
400
401
401 if compare or (at_version and not at_version == PullRequest.LATEST_VER):
402 if compare or (at_version and not at_version == PullRequest.LATEST_VER):
402 c.allowed_to_change_status = False
403 c.allowed_to_change_status = False
403 c.allowed_to_update = False
404 c.allowed_to_update = False
404 c.allowed_to_merge = False
405 c.allowed_to_merge = False
405 c.allowed_to_delete = False
406 c.allowed_to_delete = False
406 c.allowed_to_comment = False
407 c.allowed_to_comment = False
407 c.allowed_to_close = False
408 c.allowed_to_close = False
408 else:
409 else:
409 can_change_status = PullRequestModel().check_user_change_status(
410 can_change_status = PullRequestModel().check_user_change_status(
410 pull_request_at_ver, self._rhodecode_user)
411 pull_request_at_ver, self._rhodecode_user)
411 c.allowed_to_change_status = can_change_status and not pr_closed
412 c.allowed_to_change_status = can_change_status and not pr_closed
412
413
413 c.allowed_to_update = PullRequestModel().check_user_update(
414 c.allowed_to_update = PullRequestModel().check_user_update(
414 pull_request_latest, self._rhodecode_user) and not pr_closed
415 pull_request_latest, self._rhodecode_user) and not pr_closed
415 c.allowed_to_merge = PullRequestModel().check_user_merge(
416 c.allowed_to_merge = PullRequestModel().check_user_merge(
416 pull_request_latest, self._rhodecode_user) and not pr_closed
417 pull_request_latest, self._rhodecode_user) and not pr_closed
417 c.allowed_to_delete = PullRequestModel().check_user_delete(
418 c.allowed_to_delete = PullRequestModel().check_user_delete(
418 pull_request_latest, self._rhodecode_user) and not pr_closed
419 pull_request_latest, self._rhodecode_user) and not pr_closed
419 c.allowed_to_comment = not pr_closed
420 c.allowed_to_comment = not pr_closed
420 c.allowed_to_close = c.allowed_to_merge and not pr_closed
421 c.allowed_to_close = c.allowed_to_merge and not pr_closed
421
422
422 c.forbid_adding_reviewers = False
423 c.forbid_adding_reviewers = False
423 c.forbid_author_to_review = False
424 c.forbid_author_to_review = False
424 c.forbid_commit_author_to_review = False
425 c.forbid_commit_author_to_review = False
425
426
426 if pull_request_latest.reviewer_data and \
427 if pull_request_latest.reviewer_data and \
427 'rules' in pull_request_latest.reviewer_data:
428 'rules' in pull_request_latest.reviewer_data:
428 rules = pull_request_latest.reviewer_data['rules'] or {}
429 rules = pull_request_latest.reviewer_data['rules'] or {}
429 try:
430 try:
430 c.forbid_adding_reviewers = rules.get('forbid_adding_reviewers')
431 c.forbid_adding_reviewers = rules.get('forbid_adding_reviewers')
431 c.forbid_author_to_review = rules.get('forbid_author_to_review')
432 c.forbid_author_to_review = rules.get('forbid_author_to_review')
432 c.forbid_commit_author_to_review = rules.get('forbid_commit_author_to_review')
433 c.forbid_commit_author_to_review = rules.get('forbid_commit_author_to_review')
433 except Exception:
434 except Exception:
434 pass
435 pass
435
436
436 # check merge capabilities
437 # check merge capabilities
437 _merge_check = MergeCheck.validate(
438 _merge_check = MergeCheck.validate(
438 pull_request_latest, auth_user=self._rhodecode_user,
439 pull_request_latest, auth_user=self._rhodecode_user,
439 translator=self.request.translate,
440 translator=self.request.translate,
440 force_shadow_repo_refresh=force_refresh)
441 force_shadow_repo_refresh=force_refresh)
441
442
442 c.pr_merge_errors = _merge_check.error_details
443 c.pr_merge_errors = _merge_check.error_details
443 c.pr_merge_possible = not _merge_check.failed
444 c.pr_merge_possible = not _merge_check.failed
444 c.pr_merge_message = _merge_check.merge_msg
445 c.pr_merge_message = _merge_check.merge_msg
445 c.pr_merge_source_commit = _merge_check.source_commit
446 c.pr_merge_source_commit = _merge_check.source_commit
446 c.pr_merge_target_commit = _merge_check.target_commit
447 c.pr_merge_target_commit = _merge_check.target_commit
447
448
448 c.pr_merge_info = MergeCheck.get_merge_conditions(
449 c.pr_merge_info = MergeCheck.get_merge_conditions(
449 pull_request_latest, translator=self.request.translate)
450 pull_request_latest, translator=self.request.translate)
450
451
451 c.pull_request_review_status = _merge_check.review_status
452 c.pull_request_review_status = _merge_check.review_status
452 if merge_checks:
453 if merge_checks:
453 self.request.override_renderer = \
454 self.request.override_renderer = \
454 'rhodecode:templates/pullrequests/pullrequest_merge_checks.mako'
455 'rhodecode:templates/pullrequests/pullrequest_merge_checks.mako'
455 return self._get_template_context(c)
456 return self._get_template_context(c)
456
457
457 c.allowed_reviewers = [obj.user_id for obj in pull_request.reviewers if obj.user]
458 c.allowed_reviewers = [obj.user_id for obj in pull_request.reviewers if obj.user]
458 c.reviewers_count = pull_request.reviewers_count
459 c.reviewers_count = pull_request.reviewers_count
459 c.observers_count = pull_request.observers_count
460 c.observers_count = pull_request.observers_count
460
461
461 # reviewers and statuses
462 # reviewers and statuses
462 c.pull_request_default_reviewers_data_json = json.dumps(pull_request.reviewer_data)
463 c.pull_request_default_reviewers_data_json = json.dumps(pull_request.reviewer_data)
463 c.pull_request_set_reviewers_data_json = collections.OrderedDict({'reviewers': []})
464 c.pull_request_set_reviewers_data_json = collections.OrderedDict({'reviewers': []})
464 c.pull_request_set_observers_data_json = collections.OrderedDict({'observers': []})
465 c.pull_request_set_observers_data_json = collections.OrderedDict({'observers': []})
465
466
466 for review_obj, member, reasons, mandatory, status in pull_request_at_ver.reviewers_statuses():
467 for review_obj, member, reasons, mandatory, status in pull_request_at_ver.reviewers_statuses():
467 member_reviewer = h.reviewer_as_json(
468 member_reviewer = h.reviewer_as_json(
468 member, reasons=reasons, mandatory=mandatory,
469 member, reasons=reasons, mandatory=mandatory,
469 role=review_obj.role,
470 role=review_obj.role,
470 user_group=review_obj.rule_user_group_data()
471 user_group=review_obj.rule_user_group_data()
471 )
472 )
472
473
473 current_review_status = status[0][1].status if status else ChangesetStatus.STATUS_NOT_REVIEWED
474 current_review_status = status[0][1].status if status else ChangesetStatus.STATUS_NOT_REVIEWED
474 member_reviewer['review_status'] = current_review_status
475 member_reviewer['review_status'] = current_review_status
475 member_reviewer['review_status_label'] = h.commit_status_lbl(current_review_status)
476 member_reviewer['review_status_label'] = h.commit_status_lbl(current_review_status)
476 member_reviewer['allowed_to_update'] = c.allowed_to_update
477 member_reviewer['allowed_to_update'] = c.allowed_to_update
477 c.pull_request_set_reviewers_data_json['reviewers'].append(member_reviewer)
478 c.pull_request_set_reviewers_data_json['reviewers'].append(member_reviewer)
478
479
479 c.pull_request_set_reviewers_data_json = json.dumps(c.pull_request_set_reviewers_data_json)
480 c.pull_request_set_reviewers_data_json = json.dumps(c.pull_request_set_reviewers_data_json)
480
481
481 for observer_obj, member in pull_request_at_ver.observers():
482 for observer_obj, member in pull_request_at_ver.observers():
482 member_observer = h.reviewer_as_json(
483 member_observer = h.reviewer_as_json(
483 member, reasons=[], mandatory=False,
484 member, reasons=[], mandatory=False,
484 role=observer_obj.role,
485 role=observer_obj.role,
485 user_group=observer_obj.rule_user_group_data()
486 user_group=observer_obj.rule_user_group_data()
486 )
487 )
487 member_observer['allowed_to_update'] = c.allowed_to_update
488 member_observer['allowed_to_update'] = c.allowed_to_update
488 c.pull_request_set_observers_data_json['observers'].append(member_observer)
489 c.pull_request_set_observers_data_json['observers'].append(member_observer)
489
490
490 c.pull_request_set_observers_data_json = json.dumps(c.pull_request_set_observers_data_json)
491 c.pull_request_set_observers_data_json = json.dumps(c.pull_request_set_observers_data_json)
491
492
492 general_comments, inline_comments = \
493 general_comments, inline_comments = \
493 self.register_comments_vars(c, pull_request_latest, versions)
494 self.register_comments_vars(c, pull_request_latest, versions)
494
495
495 # TODOs
496 # TODOs
496 c.unresolved_comments = CommentsModel() \
497 c.unresolved_comments = CommentsModel() \
497 .get_pull_request_unresolved_todos(pull_request_latest)
498 .get_pull_request_unresolved_todos(pull_request_latest)
498 c.resolved_comments = CommentsModel() \
499 c.resolved_comments = CommentsModel() \
499 .get_pull_request_resolved_todos(pull_request_latest)
500 .get_pull_request_resolved_todos(pull_request_latest)
500
501
501 # if we use version, then do not show later comments
502 # if we use version, then do not show later comments
502 # than current version
503 # than current version
503 display_inline_comments = collections.defaultdict(
504 display_inline_comments = collections.defaultdict(
504 lambda: collections.defaultdict(list))
505 lambda: collections.defaultdict(list))
505 for co in inline_comments:
506 for co in inline_comments:
506 if c.at_version_num:
507 if c.at_version_num:
507 # pick comments that are at least UPTO given version, so we
508 # pick comments that are at least UPTO given version, so we
508 # don't render comments for higher version
509 # don't render comments for higher version
509 should_render = co.pull_request_version_id and \
510 should_render = co.pull_request_version_id and \
510 co.pull_request_version_id <= c.at_version_num
511 co.pull_request_version_id <= c.at_version_num
511 else:
512 else:
512 # showing all, for 'latest'
513 # showing all, for 'latest'
513 should_render = True
514 should_render = True
514
515
515 if should_render:
516 if should_render:
516 display_inline_comments[co.f_path][co.line_no].append(co)
517 display_inline_comments[co.f_path][co.line_no].append(co)
517
518
518 # load diff data into template context, if we use compare mode then
519 # load diff data into template context, if we use compare mode then
519 # diff is calculated based on changes between versions of PR
520 # diff is calculated based on changes between versions of PR
520
521
521 source_repo = pull_request_at_ver.source_repo
522 source_repo = pull_request_at_ver.source_repo
522 source_ref_id = pull_request_at_ver.source_ref_parts.commit_id
523 source_ref_id = pull_request_at_ver.source_ref_parts.commit_id
523
524
524 target_repo = pull_request_at_ver.target_repo
525 target_repo = pull_request_at_ver.target_repo
525 target_ref_id = pull_request_at_ver.target_ref_parts.commit_id
526 target_ref_id = pull_request_at_ver.target_ref_parts.commit_id
526
527
527 if compare:
528 if compare:
528 # in compare switch the diff base to latest commit from prev version
529 # in compare switch the diff base to latest commit from prev version
529 target_ref_id = prev_pull_request_display_obj.revisions[0]
530 target_ref_id = prev_pull_request_display_obj.revisions[0]
530
531
531 # despite opening commits for bookmarks/branches/tags, we always
532 # despite opening commits for bookmarks/branches/tags, we always
532 # convert this to rev to prevent changes after bookmark or branch change
533 # convert this to rev to prevent changes after bookmark or branch change
533 c.source_ref_type = 'rev'
534 c.source_ref_type = 'rev'
534 c.source_ref = source_ref_id
535 c.source_ref = source_ref_id
535
536
536 c.target_ref_type = 'rev'
537 c.target_ref_type = 'rev'
537 c.target_ref = target_ref_id
538 c.target_ref = target_ref_id
538
539
539 c.source_repo = source_repo
540 c.source_repo = source_repo
540 c.target_repo = target_repo
541 c.target_repo = target_repo
541
542
542 c.commit_ranges = []
543 c.commit_ranges = []
543 source_commit = EmptyCommit()
544 source_commit = EmptyCommit()
544 target_commit = EmptyCommit()
545 target_commit = EmptyCommit()
545 c.missing_requirements = False
546 c.missing_requirements = False
546
547
547 source_scm = source_repo.scm_instance()
548 source_scm = source_repo.scm_instance()
548 target_scm = target_repo.scm_instance()
549 target_scm = target_repo.scm_instance()
549
550
550 shadow_scm = None
551 shadow_scm = None
551 try:
552 try:
552 shadow_scm = pull_request_latest.get_shadow_repo()
553 shadow_scm = pull_request_latest.get_shadow_repo()
553 except Exception:
554 except Exception:
554 log.debug('Failed to get shadow repo', exc_info=True)
555 log.debug('Failed to get shadow repo', exc_info=True)
555 # try first the existing source_repo, and then shadow
556 # try first the existing source_repo, and then shadow
556 # repo if we can obtain one
557 # repo if we can obtain one
557 commits_source_repo = source_scm
558 commits_source_repo = source_scm
558 if shadow_scm:
559 if shadow_scm:
559 commits_source_repo = shadow_scm
560 commits_source_repo = shadow_scm
560
561
561 c.commits_source_repo = commits_source_repo
562 c.commits_source_repo = commits_source_repo
562 c.ancestor = None # set it to None, to hide it from PR view
563 c.ancestor = None # set it to None, to hide it from PR view
563
564
564 # empty version means latest, so we keep this to prevent
565 # empty version means latest, so we keep this to prevent
565 # double caching
566 # double caching
566 version_normalized = version or PullRequest.LATEST_VER
567 version_normalized = version or PullRequest.LATEST_VER
567 from_version_normalized = from_version or PullRequest.LATEST_VER
568 from_version_normalized = from_version or PullRequest.LATEST_VER
568
569
569 cache_path = self.rhodecode_vcs_repo.get_create_shadow_cache_pr_path(target_repo)
570 cache_path = self.rhodecode_vcs_repo.get_create_shadow_cache_pr_path(target_repo)
570 cache_file_path = diff_cache_exist(
571 cache_file_path = diff_cache_exist(
571 cache_path, 'pull_request', pull_request_id, version_normalized,
572 cache_path, 'pull_request', pull_request_id, version_normalized,
572 from_version_normalized, source_ref_id, target_ref_id,
573 from_version_normalized, source_ref_id, target_ref_id,
573 hide_whitespace_changes, diff_context, c.fulldiff)
574 hide_whitespace_changes, diff_context, c.fulldiff)
574
575
575 caching_enabled = self._is_diff_cache_enabled(c.target_repo)
576 caching_enabled = self._is_diff_cache_enabled(c.target_repo)
576 force_recache = self.get_recache_flag()
577 force_recache = self.get_recache_flag()
577
578
578 cached_diff = None
579 cached_diff = None
579 if caching_enabled:
580 if caching_enabled:
580 cached_diff = load_cached_diff(cache_file_path)
581 cached_diff = load_cached_diff(cache_file_path)
581
582
582 has_proper_commit_cache = (
583 has_proper_commit_cache = (
583 cached_diff and cached_diff.get('commits')
584 cached_diff and cached_diff.get('commits')
584 and len(cached_diff.get('commits', [])) == 5
585 and len(cached_diff.get('commits', [])) == 5
585 and cached_diff.get('commits')[0]
586 and cached_diff.get('commits')[0]
586 and cached_diff.get('commits')[3])
587 and cached_diff.get('commits')[3])
587
588
588 if not force_recache and not c.range_diff_on and has_proper_commit_cache:
589 if not force_recache and not c.range_diff_on and has_proper_commit_cache:
589 diff_commit_cache = \
590 diff_commit_cache = \
590 (ancestor_commit, commit_cache, missing_requirements,
591 (ancestor_commit, commit_cache, missing_requirements,
591 source_commit, target_commit) = cached_diff['commits']
592 source_commit, target_commit) = cached_diff['commits']
592 else:
593 else:
593 # NOTE(marcink): we reach potentially unreachable errors when a PR has
594 # NOTE(marcink): we reach potentially unreachable errors when a PR has
594 # merge errors resulting in potentially hidden commits in the shadow repo.
595 # merge errors resulting in potentially hidden commits in the shadow repo.
595 maybe_unreachable = _merge_check.MERGE_CHECK in _merge_check.error_details \
596 maybe_unreachable = _merge_check.MERGE_CHECK in _merge_check.error_details \
596 and _merge_check.merge_response
597 and _merge_check.merge_response
597 maybe_unreachable = maybe_unreachable \
598 maybe_unreachable = maybe_unreachable \
598 and _merge_check.merge_response.metadata.get('unresolved_files')
599 and _merge_check.merge_response.metadata.get('unresolved_files')
599 log.debug("Using unreachable commits due to MERGE_CHECK in merge simulation")
600 log.debug("Using unreachable commits due to MERGE_CHECK in merge simulation")
600 diff_commit_cache = \
601 diff_commit_cache = \
601 (ancestor_commit, commit_cache, missing_requirements,
602 (ancestor_commit, commit_cache, missing_requirements,
602 source_commit, target_commit) = self.get_commits(
603 source_commit, target_commit) = self.get_commits(
603 commits_source_repo,
604 commits_source_repo,
604 pull_request_at_ver,
605 pull_request_at_ver,
605 source_commit,
606 source_commit,
606 source_ref_id,
607 source_ref_id,
607 source_scm,
608 source_scm,
608 target_commit,
609 target_commit,
609 target_ref_id,
610 target_ref_id,
610 target_scm,
611 target_scm,
611 maybe_unreachable=maybe_unreachable)
612 maybe_unreachable=maybe_unreachable)
612
613
613 # register our commit range
614 # register our commit range
614 for comm in commit_cache.values():
615 for comm in commit_cache.values():
615 c.commit_ranges.append(comm)
616 c.commit_ranges.append(comm)
616
617
617 c.missing_requirements = missing_requirements
618 c.missing_requirements = missing_requirements
618 c.ancestor_commit = ancestor_commit
619 c.ancestor_commit = ancestor_commit
619 c.statuses = source_repo.statuses(
620 c.statuses = source_repo.statuses(
620 [x.raw_id for x in c.commit_ranges])
621 [x.raw_id for x in c.commit_ranges])
621
622
622 # auto collapse if we have more than limit
623 # auto collapse if we have more than limit
623 collapse_limit = diffs.DiffProcessor._collapse_commits_over
624 collapse_limit = diffs.DiffProcessor._collapse_commits_over
624 c.collapse_all_commits = len(c.commit_ranges) > collapse_limit
625 c.collapse_all_commits = len(c.commit_ranges) > collapse_limit
625 c.compare_mode = compare
626 c.compare_mode = compare
626
627
627 # diff_limit is the old behavior, will cut off the whole diff
628 # diff_limit is the old behavior, will cut off the whole diff
628 # if the limit is applied otherwise will just hide the
629 # if the limit is applied otherwise will just hide the
629 # big files from the front-end
630 # big files from the front-end
630 diff_limit = c.visual.cut_off_limit_diff
631 diff_limit = c.visual.cut_off_limit_diff
631 file_limit = c.visual.cut_off_limit_file
632 file_limit = c.visual.cut_off_limit_file
632
633
633 c.missing_commits = False
634 c.missing_commits = False
634 if (c.missing_requirements
635 if (c.missing_requirements
635 or isinstance(source_commit, EmptyCommit)
636 or isinstance(source_commit, EmptyCommit)
636 or source_commit == target_commit):
637 or source_commit == target_commit):
637
638
638 c.missing_commits = True
639 c.missing_commits = True
639 else:
640 else:
640 c.inline_comments = display_inline_comments
641 c.inline_comments = display_inline_comments
641
642
642 use_ancestor = True
643 use_ancestor = True
643 if from_version_normalized != version_normalized:
644 if from_version_normalized != version_normalized:
644 use_ancestor = False
645 use_ancestor = False
645
646
646 has_proper_diff_cache = cached_diff and cached_diff.get('commits')
647 has_proper_diff_cache = cached_diff and cached_diff.get('commits')
647 if not force_recache and has_proper_diff_cache:
648 if not force_recache and has_proper_diff_cache:
648 c.diffset = cached_diff['diff']
649 c.diffset = cached_diff['diff']
649 else:
650 else:
650 try:
651 try:
651 c.diffset = self._get_diffset(
652 c.diffset = self._get_diffset(
652 c.source_repo.repo_name, commits_source_repo,
653 c.source_repo.repo_name, commits_source_repo,
653 c.ancestor_commit,
654 c.ancestor_commit,
654 source_ref_id, target_ref_id,
655 source_ref_id, target_ref_id,
655 target_commit, source_commit,
656 target_commit, source_commit,
656 diff_limit, file_limit, c.fulldiff,
657 diff_limit, file_limit, c.fulldiff,
657 hide_whitespace_changes, diff_context,
658 hide_whitespace_changes, diff_context,
658 use_ancestor=use_ancestor
659 use_ancestor=use_ancestor
659 )
660 )
660
661
661 # save cached diff
662 # save cached diff
662 if caching_enabled:
663 if caching_enabled:
663 cache_diff(cache_file_path, c.diffset, diff_commit_cache)
664 cache_diff(cache_file_path, c.diffset, diff_commit_cache)
664 except CommitDoesNotExistError:
665 except CommitDoesNotExistError:
665 log.exception('Failed to generate diffset')
666 log.exception('Failed to generate diffset')
666 c.missing_commits = True
667 c.missing_commits = True
667
668
668 if not c.missing_commits:
669 if not c.missing_commits:
669
670
670 c.limited_diff = c.diffset.limited_diff
671 c.limited_diff = c.diffset.limited_diff
671
672
672 # calculate removed files that are bound to comments
673 # calculate removed files that are bound to comments
673 comment_deleted_files = [
674 comment_deleted_files = [
674 fname for fname in display_inline_comments
675 fname for fname in display_inline_comments
675 if fname not in c.diffset.file_stats]
676 if fname not in c.diffset.file_stats]
676
677
677 c.deleted_files_comments = collections.defaultdict(dict)
678 c.deleted_files_comments = collections.defaultdict(dict)
678 for fname, per_line_comments in display_inline_comments.items():
679 for fname, per_line_comments in display_inline_comments.items():
679 if fname in comment_deleted_files:
680 if fname in comment_deleted_files:
680 c.deleted_files_comments[fname]['stats'] = 0
681 c.deleted_files_comments[fname]['stats'] = 0
681 c.deleted_files_comments[fname]['comments'] = list()
682 c.deleted_files_comments[fname]['comments'] = list()
682 for lno, comments in per_line_comments.items():
683 for lno, comments in per_line_comments.items():
683 c.deleted_files_comments[fname]['comments'].extend(comments)
684 c.deleted_files_comments[fname]['comments'].extend(comments)
684
685
685 # maybe calculate the range diff
686 # maybe calculate the range diff
686 if c.range_diff_on:
687 if c.range_diff_on:
687 # TODO(marcink): set whitespace/context
688 # TODO(marcink): set whitespace/context
688 context_lcl = 3
689 context_lcl = 3
689 ign_whitespace_lcl = False
690 ign_whitespace_lcl = False
690
691
691 for commit in c.commit_ranges:
692 for commit in c.commit_ranges:
692 commit2 = commit
693 commit2 = commit
693 commit1 = commit.first_parent
694 commit1 = commit.first_parent
694
695
695 range_diff_cache_file_path = diff_cache_exist(
696 range_diff_cache_file_path = diff_cache_exist(
696 cache_path, 'diff', commit.raw_id,
697 cache_path, 'diff', commit.raw_id,
697 ign_whitespace_lcl, context_lcl, c.fulldiff)
698 ign_whitespace_lcl, context_lcl, c.fulldiff)
698
699
699 cached_diff = None
700 cached_diff = None
700 if caching_enabled:
701 if caching_enabled:
701 cached_diff = load_cached_diff(range_diff_cache_file_path)
702 cached_diff = load_cached_diff(range_diff_cache_file_path)
702
703
703 has_proper_diff_cache = cached_diff and cached_diff.get('diff')
704 has_proper_diff_cache = cached_diff and cached_diff.get('diff')
704 if not force_recache and has_proper_diff_cache:
705 if not force_recache and has_proper_diff_cache:
705 diffset = cached_diff['diff']
706 diffset = cached_diff['diff']
706 else:
707 else:
707 diffset = self._get_range_diffset(
708 diffset = self._get_range_diffset(
708 commits_source_repo, source_repo,
709 commits_source_repo, source_repo,
709 commit1, commit2, diff_limit, file_limit,
710 commit1, commit2, diff_limit, file_limit,
710 c.fulldiff, ign_whitespace_lcl, context_lcl
711 c.fulldiff, ign_whitespace_lcl, context_lcl
711 )
712 )
712
713
713 # save cached diff
714 # save cached diff
714 if caching_enabled:
715 if caching_enabled:
715 cache_diff(range_diff_cache_file_path, diffset, None)
716 cache_diff(range_diff_cache_file_path, diffset, None)
716
717
717 c.changes[commit.raw_id] = diffset
718 c.changes[commit.raw_id] = diffset
718
719
719 # this is a hack to properly display links, when creating PR, the
720 # this is a hack to properly display links, when creating PR, the
720 # compare view and others uses different notation, and
721 # compare view and others uses different notation, and
721 # compare_commits.mako renders links based on the target_repo.
722 # compare_commits.mako renders links based on the target_repo.
722 # We need to swap that here to generate it properly on the html side
723 # We need to swap that here to generate it properly on the html side
723 c.target_repo = c.source_repo
724 c.target_repo = c.source_repo
724
725
725 c.commit_statuses = ChangesetStatus.STATUSES
726 c.commit_statuses = ChangesetStatus.STATUSES
726
727
727 c.show_version_changes = not pr_closed
728 c.show_version_changes = not pr_closed
728 if c.show_version_changes:
729 if c.show_version_changes:
729 cur_obj = pull_request_at_ver
730 cur_obj = pull_request_at_ver
730 prev_obj = prev_pull_request_at_ver
731 prev_obj = prev_pull_request_at_ver
731
732
732 old_commit_ids = prev_obj.revisions
733 old_commit_ids = prev_obj.revisions
733 new_commit_ids = cur_obj.revisions
734 new_commit_ids = cur_obj.revisions
734 commit_changes = PullRequestModel()._calculate_commit_id_changes(
735 commit_changes = PullRequestModel()._calculate_commit_id_changes(
735 old_commit_ids, new_commit_ids)
736 old_commit_ids, new_commit_ids)
736 c.commit_changes_summary = commit_changes
737 c.commit_changes_summary = commit_changes
737
738
738 # calculate the diff for commits between versions
739 # calculate the diff for commits between versions
739 c.commit_changes = []
740 c.commit_changes = []
740
741
741 def mark(cs, fw):
742 def mark(cs, fw):
742 return list(h.itertools.izip_longest([], cs, fillvalue=fw))
743 return list(h.itertools.izip_longest([], cs, fillvalue=fw))
743
744
744 for c_type, raw_id in mark(commit_changes.added, 'a') \
745 for c_type, raw_id in mark(commit_changes.added, 'a') \
745 + mark(commit_changes.removed, 'r') \
746 + mark(commit_changes.removed, 'r') \
746 + mark(commit_changes.common, 'c'):
747 + mark(commit_changes.common, 'c'):
747
748
748 if raw_id in commit_cache:
749 if raw_id in commit_cache:
749 commit = commit_cache[raw_id]
750 commit = commit_cache[raw_id]
750 else:
751 else:
751 try:
752 try:
752 commit = commits_source_repo.get_commit(raw_id)
753 commit = commits_source_repo.get_commit(raw_id)
753 except CommitDoesNotExistError:
754 except CommitDoesNotExistError:
754 # in case we fail extracting still use "dummy" commit
755 # in case we fail extracting still use "dummy" commit
755 # for display in commit diff
756 # for display in commit diff
756 commit = h.AttributeDict(
757 commit = h.AttributeDict(
757 {'raw_id': raw_id,
758 {'raw_id': raw_id,
758 'message': 'EMPTY or MISSING COMMIT'})
759 'message': 'EMPTY or MISSING COMMIT'})
759 c.commit_changes.append([c_type, commit])
760 c.commit_changes.append([c_type, commit])
760
761
761 # current user review statuses for each version
762 # current user review statuses for each version
762 c.review_versions = {}
763 c.review_versions = {}
763 if self._rhodecode_user.user_id in c.allowed_reviewers:
764 if self._rhodecode_user.user_id in c.allowed_reviewers:
764 for co in general_comments:
765 for co in general_comments:
765 if co.author.user_id == self._rhodecode_user.user_id:
766 if co.author.user_id == self._rhodecode_user.user_id:
766 status = co.status_change
767 status = co.status_change
767 if status:
768 if status:
768 _ver_pr = status[0].comment.pull_request_version_id
769 _ver_pr = status[0].comment.pull_request_version_id
769 c.review_versions[_ver_pr] = status[0]
770 c.review_versions[_ver_pr] = status[0]
770
771
771 return self._get_template_context(c)
772 return self._get_template_context(c)
772
773
773 def get_commits(
774 def get_commits(
774 self, commits_source_repo, pull_request_at_ver, source_commit,
775 self, commits_source_repo, pull_request_at_ver, source_commit,
775 source_ref_id, source_scm, target_commit, target_ref_id, target_scm,
776 source_ref_id, source_scm, target_commit, target_ref_id, target_scm,
776 maybe_unreachable=False):
777 maybe_unreachable=False):
777
778
778 commit_cache = collections.OrderedDict()
779 commit_cache = collections.OrderedDict()
779 missing_requirements = False
780 missing_requirements = False
780
781
781 try:
782 try:
782 pre_load = ["author", "date", "message", "branch", "parents"]
783 pre_load = ["author", "date", "message", "branch", "parents"]
783
784
784 pull_request_commits = pull_request_at_ver.revisions
785 pull_request_commits = pull_request_at_ver.revisions
785 log.debug('Loading %s commits from %s',
786 log.debug('Loading %s commits from %s',
786 len(pull_request_commits), commits_source_repo)
787 len(pull_request_commits), commits_source_repo)
787
788
788 for rev in pull_request_commits:
789 for rev in pull_request_commits:
789 comm = commits_source_repo.get_commit(commit_id=rev, pre_load=pre_load,
790 comm = commits_source_repo.get_commit(commit_id=rev, pre_load=pre_load,
790 maybe_unreachable=maybe_unreachable)
791 maybe_unreachable=maybe_unreachable)
791 commit_cache[comm.raw_id] = comm
792 commit_cache[comm.raw_id] = comm
792
793
793 # Order here matters, we first need to get target, and then
794 # Order here matters, we first need to get target, and then
794 # the source
795 # the source
795 target_commit = commits_source_repo.get_commit(
796 target_commit = commits_source_repo.get_commit(
796 commit_id=safe_str(target_ref_id))
797 commit_id=safe_str(target_ref_id))
797
798
798 source_commit = commits_source_repo.get_commit(
799 source_commit = commits_source_repo.get_commit(
799 commit_id=safe_str(source_ref_id), maybe_unreachable=True)
800 commit_id=safe_str(source_ref_id), maybe_unreachable=True)
800 except CommitDoesNotExistError:
801 except CommitDoesNotExistError:
801 log.warning('Failed to get commit from `{}` repo'.format(
802 log.warning('Failed to get commit from `{}` repo'.format(
802 commits_source_repo), exc_info=True)
803 commits_source_repo), exc_info=True)
803 except RepositoryRequirementError:
804 except RepositoryRequirementError:
804 log.warning('Failed to get all required data from repo', exc_info=True)
805 log.warning('Failed to get all required data from repo', exc_info=True)
805 missing_requirements = True
806 missing_requirements = True
806
807
807 pr_ancestor_id = pull_request_at_ver.common_ancestor_id
808 pr_ancestor_id = pull_request_at_ver.common_ancestor_id
808
809
809 try:
810 try:
810 ancestor_commit = source_scm.get_commit(pr_ancestor_id)
811 ancestor_commit = source_scm.get_commit(pr_ancestor_id)
811 except Exception:
812 except Exception:
812 ancestor_commit = None
813 ancestor_commit = None
813
814
814 return ancestor_commit, commit_cache, missing_requirements, source_commit, target_commit
815 return ancestor_commit, commit_cache, missing_requirements, source_commit, target_commit
815
816
816 def assure_not_empty_repo(self):
817 def assure_not_empty_repo(self):
817 _ = self.request.translate
818 _ = self.request.translate
818
819
819 try:
820 try:
820 self.db_repo.scm_instance().get_commit()
821 self.db_repo.scm_instance().get_commit()
821 except EmptyRepositoryError:
822 except EmptyRepositoryError:
822 h.flash(h.literal(_('There are no commits yet')),
823 h.flash(h.literal(_('There are no commits yet')),
823 category='warning')
824 category='warning')
824 raise HTTPFound(
825 raise HTTPFound(
825 h.route_path('repo_summary', repo_name=self.db_repo.repo_name))
826 h.route_path('repo_summary', repo_name=self.db_repo.repo_name))
826
827
827 @LoginRequired()
828 @LoginRequired()
828 @NotAnonymous()
829 @NotAnonymous()
829 @HasRepoPermissionAnyDecorator(
830 @HasRepoPermissionAnyDecorator(
830 'repository.read', 'repository.write', 'repository.admin')
831 'repository.read', 'repository.write', 'repository.admin')
831 @view_config(
832 @view_config(
832 route_name='pullrequest_new', request_method='GET',
833 route_name='pullrequest_new', request_method='GET',
833 renderer='rhodecode:templates/pullrequests/pullrequest.mako')
834 renderer='rhodecode:templates/pullrequests/pullrequest.mako')
834 def pull_request_new(self):
835 def pull_request_new(self):
835 _ = self.request.translate
836 _ = self.request.translate
836 c = self.load_default_context()
837 c = self.load_default_context()
837
838
838 self.assure_not_empty_repo()
839 self.assure_not_empty_repo()
839 source_repo = self.db_repo
840 source_repo = self.db_repo
840
841
841 commit_id = self.request.GET.get('commit')
842 commit_id = self.request.GET.get('commit')
842 branch_ref = self.request.GET.get('branch')
843 branch_ref = self.request.GET.get('branch')
843 bookmark_ref = self.request.GET.get('bookmark')
844 bookmark_ref = self.request.GET.get('bookmark')
844
845
845 try:
846 try:
846 source_repo_data = PullRequestModel().generate_repo_data(
847 source_repo_data = PullRequestModel().generate_repo_data(
847 source_repo, commit_id=commit_id,
848 source_repo, commit_id=commit_id,
848 branch=branch_ref, bookmark=bookmark_ref,
849 branch=branch_ref, bookmark=bookmark_ref,
849 translator=self.request.translate)
850 translator=self.request.translate)
850 except CommitDoesNotExistError as e:
851 except CommitDoesNotExistError as e:
851 log.exception(e)
852 log.exception(e)
852 h.flash(_('Commit does not exist'), 'error')
853 h.flash(_('Commit does not exist'), 'error')
853 raise HTTPFound(
854 raise HTTPFound(
854 h.route_path('pullrequest_new', repo_name=source_repo.repo_name))
855 h.route_path('pullrequest_new', repo_name=source_repo.repo_name))
855
856
856 default_target_repo = source_repo
857 default_target_repo = source_repo
857
858
858 if source_repo.parent and c.has_origin_repo_read_perm:
859 if source_repo.parent and c.has_origin_repo_read_perm:
859 parent_vcs_obj = source_repo.parent.scm_instance()
860 parent_vcs_obj = source_repo.parent.scm_instance()
860 if parent_vcs_obj and not parent_vcs_obj.is_empty():
861 if parent_vcs_obj and not parent_vcs_obj.is_empty():
861 # change default if we have a parent repo
862 # change default if we have a parent repo
862 default_target_repo = source_repo.parent
863 default_target_repo = source_repo.parent
863
864
864 target_repo_data = PullRequestModel().generate_repo_data(
865 target_repo_data = PullRequestModel().generate_repo_data(
865 default_target_repo, translator=self.request.translate)
866 default_target_repo, translator=self.request.translate)
866
867
867 selected_source_ref = source_repo_data['refs']['selected_ref']
868 selected_source_ref = source_repo_data['refs']['selected_ref']
868 title_source_ref = ''
869 title_source_ref = ''
869 if selected_source_ref:
870 if selected_source_ref:
870 title_source_ref = selected_source_ref.split(':', 2)[1]
871 title_source_ref = selected_source_ref.split(':', 2)[1]
871 c.default_title = PullRequestModel().generate_pullrequest_title(
872 c.default_title = PullRequestModel().generate_pullrequest_title(
872 source=source_repo.repo_name,
873 source=source_repo.repo_name,
873 source_ref=title_source_ref,
874 source_ref=title_source_ref,
874 target=default_target_repo.repo_name
875 target=default_target_repo.repo_name
875 )
876 )
876
877
877 c.default_repo_data = {
878 c.default_repo_data = {
878 'source_repo_name': source_repo.repo_name,
879 'source_repo_name': source_repo.repo_name,
879 'source_refs_json': json.dumps(source_repo_data),
880 'source_refs_json': json.dumps(source_repo_data),
880 'target_repo_name': default_target_repo.repo_name,
881 'target_repo_name': default_target_repo.repo_name,
881 'target_refs_json': json.dumps(target_repo_data),
882 'target_refs_json': json.dumps(target_repo_data),
882 }
883 }
883 c.default_source_ref = selected_source_ref
884 c.default_source_ref = selected_source_ref
884
885
885 return self._get_template_context(c)
886 return self._get_template_context(c)
886
887
887 @LoginRequired()
888 @LoginRequired()
888 @NotAnonymous()
889 @NotAnonymous()
889 @HasRepoPermissionAnyDecorator(
890 @HasRepoPermissionAnyDecorator(
890 'repository.read', 'repository.write', 'repository.admin')
891 'repository.read', 'repository.write', 'repository.admin')
891 @view_config(
892 @view_config(
892 route_name='pullrequest_repo_refs', request_method='GET',
893 route_name='pullrequest_repo_refs', request_method='GET',
893 renderer='json_ext', xhr=True)
894 renderer='json_ext', xhr=True)
894 def pull_request_repo_refs(self):
895 def pull_request_repo_refs(self):
895 self.load_default_context()
896 self.load_default_context()
896 target_repo_name = self.request.matchdict['target_repo_name']
897 target_repo_name = self.request.matchdict['target_repo_name']
897 repo = Repository.get_by_repo_name(target_repo_name)
898 repo = Repository.get_by_repo_name(target_repo_name)
898 if not repo:
899 if not repo:
899 raise HTTPNotFound()
900 raise HTTPNotFound()
900
901
901 target_perm = HasRepoPermissionAny(
902 target_perm = HasRepoPermissionAny(
902 'repository.read', 'repository.write', 'repository.admin')(
903 'repository.read', 'repository.write', 'repository.admin')(
903 target_repo_name)
904 target_repo_name)
904 if not target_perm:
905 if not target_perm:
905 raise HTTPNotFound()
906 raise HTTPNotFound()
906
907
907 return PullRequestModel().generate_repo_data(
908 return PullRequestModel().generate_repo_data(
908 repo, translator=self.request.translate)
909 repo, translator=self.request.translate)
909
910
910 @LoginRequired()
911 @LoginRequired()
911 @NotAnonymous()
912 @NotAnonymous()
912 @HasRepoPermissionAnyDecorator(
913 @HasRepoPermissionAnyDecorator(
913 'repository.read', 'repository.write', 'repository.admin')
914 'repository.read', 'repository.write', 'repository.admin')
914 @view_config(
915 @view_config(
915 route_name='pullrequest_repo_targets', request_method='GET',
916 route_name='pullrequest_repo_targets', request_method='GET',
916 renderer='json_ext', xhr=True)
917 renderer='json_ext', xhr=True)
917 def pullrequest_repo_targets(self):
918 def pullrequest_repo_targets(self):
918 _ = self.request.translate
919 _ = self.request.translate
919 filter_query = self.request.GET.get('query')
920 filter_query = self.request.GET.get('query')
920
921
921 # get the parents
922 # get the parents
922 parent_target_repos = []
923 parent_target_repos = []
923 if self.db_repo.parent:
924 if self.db_repo.parent:
924 parents_query = Repository.query() \
925 parents_query = Repository.query() \
925 .order_by(func.length(Repository.repo_name)) \
926 .order_by(func.length(Repository.repo_name)) \
926 .filter(Repository.fork_id == self.db_repo.parent.repo_id)
927 .filter(Repository.fork_id == self.db_repo.parent.repo_id)
927
928
928 if filter_query:
929 if filter_query:
929 ilike_expression = u'%{}%'.format(safe_unicode(filter_query))
930 ilike_expression = u'%{}%'.format(safe_unicode(filter_query))
930 parents_query = parents_query.filter(
931 parents_query = parents_query.filter(
931 Repository.repo_name.ilike(ilike_expression))
932 Repository.repo_name.ilike(ilike_expression))
932 parents = parents_query.limit(20).all()
933 parents = parents_query.limit(20).all()
933
934
934 for parent in parents:
935 for parent in parents:
935 parent_vcs_obj = parent.scm_instance()
936 parent_vcs_obj = parent.scm_instance()
936 if parent_vcs_obj and not parent_vcs_obj.is_empty():
937 if parent_vcs_obj and not parent_vcs_obj.is_empty():
937 parent_target_repos.append(parent)
938 parent_target_repos.append(parent)
938
939
939 # get other forks, and repo itself
940 # get other forks, and repo itself
940 query = Repository.query() \
941 query = Repository.query() \
941 .order_by(func.length(Repository.repo_name)) \
942 .order_by(func.length(Repository.repo_name)) \
942 .filter(
943 .filter(
943 or_(Repository.repo_id == self.db_repo.repo_id, # repo itself
944 or_(Repository.repo_id == self.db_repo.repo_id, # repo itself
944 Repository.fork_id == self.db_repo.repo_id) # forks of this repo
945 Repository.fork_id == self.db_repo.repo_id) # forks of this repo
945 ) \
946 ) \
946 .filter(~Repository.repo_id.in_([x.repo_id for x in parent_target_repos]))
947 .filter(~Repository.repo_id.in_([x.repo_id for x in parent_target_repos]))
947
948
948 if filter_query:
949 if filter_query:
949 ilike_expression = u'%{}%'.format(safe_unicode(filter_query))
950 ilike_expression = u'%{}%'.format(safe_unicode(filter_query))
950 query = query.filter(Repository.repo_name.ilike(ilike_expression))
951 query = query.filter(Repository.repo_name.ilike(ilike_expression))
951
952
952 limit = max(20 - len(parent_target_repos), 5) # not less then 5
953 limit = max(20 - len(parent_target_repos), 5) # not less then 5
953 target_repos = query.limit(limit).all()
954 target_repos = query.limit(limit).all()
954
955
955 all_target_repos = target_repos + parent_target_repos
956 all_target_repos = target_repos + parent_target_repos
956
957
957 repos = []
958 repos = []
958 # This checks permissions to the repositories
959 # This checks permissions to the repositories
959 for obj in ScmModel().get_repos(all_target_repos):
960 for obj in ScmModel().get_repos(all_target_repos):
960 repos.append({
961 repos.append({
961 'id': obj['name'],
962 'id': obj['name'],
962 'text': obj['name'],
963 'text': obj['name'],
963 'type': 'repo',
964 'type': 'repo',
964 'repo_id': obj['dbrepo']['repo_id'],
965 'repo_id': obj['dbrepo']['repo_id'],
965 'repo_type': obj['dbrepo']['repo_type'],
966 'repo_type': obj['dbrepo']['repo_type'],
966 'private': obj['dbrepo']['private'],
967 'private': obj['dbrepo']['private'],
967
968
968 })
969 })
969
970
970 data = {
971 data = {
971 'more': False,
972 'more': False,
972 'results': [{
973 'results': [{
973 'text': _('Repositories'),
974 'text': _('Repositories'),
974 'children': repos
975 'children': repos
975 }] if repos else []
976 }] if repos else []
976 }
977 }
977 return data
978 return data
978
979
979 def _get_existing_ids(self, post_data):
980 def _get_existing_ids(self, post_data):
980 return filter(lambda e: e, map(safe_int, aslist(post_data.get('comments'), ',')))
981 return filter(lambda e: e, map(safe_int, aslist(post_data.get('comments'), ',')))
981
982
982 @LoginRequired()
983 @LoginRequired()
983 @NotAnonymous()
984 @NotAnonymous()
984 @HasRepoPermissionAnyDecorator(
985 @HasRepoPermissionAnyDecorator(
985 'repository.read', 'repository.write', 'repository.admin')
986 'repository.read', 'repository.write', 'repository.admin')
986 @view_config(
987 @view_config(
987 route_name='pullrequest_comments', request_method='POST',
988 route_name='pullrequest_comments', request_method='POST',
988 renderer='string_html', xhr=True)
989 renderer='string_html', xhr=True)
989 def pullrequest_comments(self):
990 def pullrequest_comments(self):
990 self.load_default_context()
991 self.load_default_context()
991
992
992 pull_request = PullRequest.get_or_404(
993 pull_request = PullRequest.get_or_404(
993 self.request.matchdict['pull_request_id'])
994 self.request.matchdict['pull_request_id'])
994 pull_request_id = pull_request.pull_request_id
995 pull_request_id = pull_request.pull_request_id
995 version = self.request.GET.get('version')
996 version = self.request.GET.get('version')
996
997
997 _render = self.request.get_partial_renderer(
998 _render = self.request.get_partial_renderer(
998 'rhodecode:templates/base/sidebar.mako')
999 'rhodecode:templates/base/sidebar.mako')
999 c = _render.get_call_context()
1000 c = _render.get_call_context()
1000
1001
1001 (pull_request_latest,
1002 (pull_request_latest,
1002 pull_request_at_ver,
1003 pull_request_at_ver,
1003 pull_request_display_obj,
1004 pull_request_display_obj,
1004 at_version) = PullRequestModel().get_pr_version(
1005 at_version) = PullRequestModel().get_pr_version(
1005 pull_request_id, version=version)
1006 pull_request_id, version=version)
1006 versions = pull_request_display_obj.versions()
1007 versions = pull_request_display_obj.versions()
1007 latest_ver = PullRequest.get_pr_display_object(pull_request_latest, pull_request_latest)
1008 latest_ver = PullRequest.get_pr_display_object(pull_request_latest, pull_request_latest)
1008 c.versions = versions + [latest_ver]
1009 c.versions = versions + [latest_ver]
1009
1010
1010 c.at_version = at_version
1011 c.at_version = at_version
1011 c.at_version_num = (at_version
1012 c.at_version_num = (at_version
1012 if at_version and at_version != PullRequest.LATEST_VER
1013 if at_version and at_version != PullRequest.LATEST_VER
1013 else None)
1014 else None)
1014
1015
1015 self.register_comments_vars(c, pull_request_latest, versions)
1016 self.register_comments_vars(c, pull_request_latest, versions)
1016 all_comments = c.inline_comments_flat + c.comments
1017 all_comments = c.inline_comments_flat + c.comments
1017
1018
1018 existing_ids = self._get_existing_ids(self.request.POST)
1019 existing_ids = self._get_existing_ids(self.request.POST)
1019 return _render('comments_table', all_comments, len(all_comments),
1020 return _render('comments_table', all_comments, len(all_comments),
1020 existing_ids=existing_ids)
1021 existing_ids=existing_ids)
1021
1022
1022 @LoginRequired()
1023 @LoginRequired()
1023 @NotAnonymous()
1024 @NotAnonymous()
1024 @HasRepoPermissionAnyDecorator(
1025 @HasRepoPermissionAnyDecorator(
1025 'repository.read', 'repository.write', 'repository.admin')
1026 'repository.read', 'repository.write', 'repository.admin')
1026 @view_config(
1027 @view_config(
1027 route_name='pullrequest_todos', request_method='POST',
1028 route_name='pullrequest_todos', request_method='POST',
1028 renderer='string_html', xhr=True)
1029 renderer='string_html', xhr=True)
1029 def pullrequest_todos(self):
1030 def pullrequest_todos(self):
1030 self.load_default_context()
1031 self.load_default_context()
1031
1032
1032 pull_request = PullRequest.get_or_404(
1033 pull_request = PullRequest.get_or_404(
1033 self.request.matchdict['pull_request_id'])
1034 self.request.matchdict['pull_request_id'])
1034 pull_request_id = pull_request.pull_request_id
1035 pull_request_id = pull_request.pull_request_id
1035 version = self.request.GET.get('version')
1036 version = self.request.GET.get('version')
1036
1037
1037 _render = self.request.get_partial_renderer(
1038 _render = self.request.get_partial_renderer(
1038 'rhodecode:templates/base/sidebar.mako')
1039 'rhodecode:templates/base/sidebar.mako')
1039 c = _render.get_call_context()
1040 c = _render.get_call_context()
1040 (pull_request_latest,
1041 (pull_request_latest,
1041 pull_request_at_ver,
1042 pull_request_at_ver,
1042 pull_request_display_obj,
1043 pull_request_display_obj,
1043 at_version) = PullRequestModel().get_pr_version(
1044 at_version) = PullRequestModel().get_pr_version(
1044 pull_request_id, version=version)
1045 pull_request_id, version=version)
1045 versions = pull_request_display_obj.versions()
1046 versions = pull_request_display_obj.versions()
1046 latest_ver = PullRequest.get_pr_display_object(pull_request_latest, pull_request_latest)
1047 latest_ver = PullRequest.get_pr_display_object(pull_request_latest, pull_request_latest)
1047 c.versions = versions + [latest_ver]
1048 c.versions = versions + [latest_ver]
1048
1049
1049 c.at_version = at_version
1050 c.at_version = at_version
1050 c.at_version_num = (at_version
1051 c.at_version_num = (at_version
1051 if at_version and at_version != PullRequest.LATEST_VER
1052 if at_version and at_version != PullRequest.LATEST_VER
1052 else None)
1053 else None)
1053
1054
1054 c.unresolved_comments = CommentsModel() \
1055 c.unresolved_comments = CommentsModel() \
1055 .get_pull_request_unresolved_todos(pull_request)
1056 .get_pull_request_unresolved_todos(pull_request)
1056 c.resolved_comments = CommentsModel() \
1057 c.resolved_comments = CommentsModel() \
1057 .get_pull_request_resolved_todos(pull_request)
1058 .get_pull_request_resolved_todos(pull_request)
1058
1059
1059 all_comments = c.unresolved_comments + c.resolved_comments
1060 all_comments = c.unresolved_comments + c.resolved_comments
1060 existing_ids = self._get_existing_ids(self.request.POST)
1061 existing_ids = self._get_existing_ids(self.request.POST)
1061 return _render('comments_table', all_comments, len(c.unresolved_comments),
1062 return _render('comments_table', all_comments, len(c.unresolved_comments),
1062 todo_comments=True, existing_ids=existing_ids)
1063 todo_comments=True, existing_ids=existing_ids)
1063
1064
1064 @LoginRequired()
1065 @LoginRequired()
1065 @NotAnonymous()
1066 @NotAnonymous()
1066 @HasRepoPermissionAnyDecorator(
1067 @HasRepoPermissionAnyDecorator(
1067 'repository.read', 'repository.write', 'repository.admin')
1068 'repository.read', 'repository.write', 'repository.admin')
1068 @CSRFRequired()
1069 @CSRFRequired()
1069 @view_config(
1070 @view_config(
1070 route_name='pullrequest_create', request_method='POST',
1071 route_name='pullrequest_create', request_method='POST',
1071 renderer=None)
1072 renderer=None)
1072 def pull_request_create(self):
1073 def pull_request_create(self):
1073 _ = self.request.translate
1074 _ = self.request.translate
1074 self.assure_not_empty_repo()
1075 self.assure_not_empty_repo()
1075 self.load_default_context()
1076 self.load_default_context()
1076
1077
1077 controls = peppercorn.parse(self.request.POST.items())
1078 controls = peppercorn.parse(self.request.POST.items())
1078
1079
1079 try:
1080 try:
1080 form = PullRequestForm(
1081 form = PullRequestForm(
1081 self.request.translate, self.db_repo.repo_id)()
1082 self.request.translate, self.db_repo.repo_id)()
1082 _form = form.to_python(controls)
1083 _form = form.to_python(controls)
1083 except formencode.Invalid as errors:
1084 except formencode.Invalid as errors:
1084 if errors.error_dict.get('revisions'):
1085 if errors.error_dict.get('revisions'):
1085 msg = 'Revisions: %s' % errors.error_dict['revisions']
1086 msg = 'Revisions: %s' % errors.error_dict['revisions']
1086 elif errors.error_dict.get('pullrequest_title'):
1087 elif errors.error_dict.get('pullrequest_title'):
1087 msg = errors.error_dict.get('pullrequest_title')
1088 msg = errors.error_dict.get('pullrequest_title')
1088 else:
1089 else:
1089 msg = _('Error creating pull request: {}').format(errors)
1090 msg = _('Error creating pull request: {}').format(errors)
1090 log.exception(msg)
1091 log.exception(msg)
1091 h.flash(msg, 'error')
1092 h.flash(msg, 'error')
1092
1093
1093 # would rather just go back to form ...
1094 # would rather just go back to form ...
1094 raise HTTPFound(
1095 raise HTTPFound(
1095 h.route_path('pullrequest_new', repo_name=self.db_repo_name))
1096 h.route_path('pullrequest_new', repo_name=self.db_repo_name))
1096
1097
1097 source_repo = _form['source_repo']
1098 source_repo = _form['source_repo']
1098 source_ref = _form['source_ref']
1099 source_ref = _form['source_ref']
1099 target_repo = _form['target_repo']
1100 target_repo = _form['target_repo']
1100 target_ref = _form['target_ref']
1101 target_ref = _form['target_ref']
1101 commit_ids = _form['revisions'][::-1]
1102 commit_ids = _form['revisions'][::-1]
1102 common_ancestor_id = _form['common_ancestor']
1103 common_ancestor_id = _form['common_ancestor']
1103
1104
1104 # find the ancestor for this pr
1105 # find the ancestor for this pr
1105 source_db_repo = Repository.get_by_repo_name(_form['source_repo'])
1106 source_db_repo = Repository.get_by_repo_name(_form['source_repo'])
1106 target_db_repo = Repository.get_by_repo_name(_form['target_repo'])
1107 target_db_repo = Repository.get_by_repo_name(_form['target_repo'])
1107
1108
1108 if not (source_db_repo or target_db_repo):
1109 if not (source_db_repo or target_db_repo):
1109 h.flash(_('source_repo or target repo not found'), category='error')
1110 h.flash(_('source_repo or target repo not found'), category='error')
1110 raise HTTPFound(
1111 raise HTTPFound(
1111 h.route_path('pullrequest_new', repo_name=self.db_repo_name))
1112 h.route_path('pullrequest_new', repo_name=self.db_repo_name))
1112
1113
1113 # re-check permissions again here
1114 # re-check permissions again here
1114 # source_repo we must have read permissions
1115 # source_repo we must have read permissions
1115
1116
1116 source_perm = HasRepoPermissionAny(
1117 source_perm = HasRepoPermissionAny(
1117 'repository.read', 'repository.write', 'repository.admin')(
1118 'repository.read', 'repository.write', 'repository.admin')(
1118 source_db_repo.repo_name)
1119 source_db_repo.repo_name)
1119 if not source_perm:
1120 if not source_perm:
1120 msg = _('Not Enough permissions to source repo `{}`.'.format(
1121 msg = _('Not Enough permissions to source repo `{}`.'.format(
1121 source_db_repo.repo_name))
1122 source_db_repo.repo_name))
1122 h.flash(msg, category='error')
1123 h.flash(msg, category='error')
1123 # copy the args back to redirect
1124 # copy the args back to redirect
1124 org_query = self.request.GET.mixed()
1125 org_query = self.request.GET.mixed()
1125 raise HTTPFound(
1126 raise HTTPFound(
1126 h.route_path('pullrequest_new', repo_name=self.db_repo_name,
1127 h.route_path('pullrequest_new', repo_name=self.db_repo_name,
1127 _query=org_query))
1128 _query=org_query))
1128
1129
1129 # target repo we must have read permissions, and also later on
1130 # target repo we must have read permissions, and also later on
1130 # we want to check branch permissions here
1131 # we want to check branch permissions here
1131 target_perm = HasRepoPermissionAny(
1132 target_perm = HasRepoPermissionAny(
1132 'repository.read', 'repository.write', 'repository.admin')(
1133 'repository.read', 'repository.write', 'repository.admin')(
1133 target_db_repo.repo_name)
1134 target_db_repo.repo_name)
1134 if not target_perm:
1135 if not target_perm:
1135 msg = _('Not Enough permissions to target repo `{}`.'.format(
1136 msg = _('Not Enough permissions to target repo `{}`.'.format(
1136 target_db_repo.repo_name))
1137 target_db_repo.repo_name))
1137 h.flash(msg, category='error')
1138 h.flash(msg, category='error')
1138 # copy the args back to redirect
1139 # copy the args back to redirect
1139 org_query = self.request.GET.mixed()
1140 org_query = self.request.GET.mixed()
1140 raise HTTPFound(
1141 raise HTTPFound(
1141 h.route_path('pullrequest_new', repo_name=self.db_repo_name,
1142 h.route_path('pullrequest_new', repo_name=self.db_repo_name,
1142 _query=org_query))
1143 _query=org_query))
1143
1144
1144 source_scm = source_db_repo.scm_instance()
1145 source_scm = source_db_repo.scm_instance()
1145 target_scm = target_db_repo.scm_instance()
1146 target_scm = target_db_repo.scm_instance()
1146
1147
1147 source_commit = source_scm.get_commit(source_ref.split(':')[-1])
1148 source_commit = source_scm.get_commit(source_ref.split(':')[-1])
1148 target_commit = target_scm.get_commit(target_ref.split(':')[-1])
1149 target_commit = target_scm.get_commit(target_ref.split(':')[-1])
1149
1150
1150 ancestor = source_scm.get_common_ancestor(
1151 ancestor = source_scm.get_common_ancestor(
1151 source_commit.raw_id, target_commit.raw_id, target_scm)
1152 source_commit.raw_id, target_commit.raw_id, target_scm)
1152
1153
1153 source_ref_type, source_ref_name, source_commit_id = _form['target_ref'].split(':')
1154 source_ref_type, source_ref_name, source_commit_id = _form['target_ref'].split(':')
1154 target_ref_type, target_ref_name, target_commit_id = _form['source_ref'].split(':')
1155 target_ref_type, target_ref_name, target_commit_id = _form['source_ref'].split(':')
1155 # recalculate target ref based on ancestor
1156 # recalculate target ref based on ancestor
1156 target_ref = ':'.join((target_ref_type, target_ref_name, ancestor))
1157 target_ref = ':'.join((target_ref_type, target_ref_name, ancestor))
1157
1158
1158 get_default_reviewers_data, validate_default_reviewers, validate_observers = \
1159 get_default_reviewers_data, validate_default_reviewers, validate_observers = \
1159 PullRequestModel().get_reviewer_functions()
1160 PullRequestModel().get_reviewer_functions()
1160
1161
1161 # recalculate reviewers logic, to make sure we can validate this
1162 # recalculate reviewers logic, to make sure we can validate this
1162 reviewer_rules = get_default_reviewers_data(
1163 reviewer_rules = get_default_reviewers_data(
1163 self._rhodecode_db_user,
1164 self._rhodecode_db_user,
1164 source_db_repo,
1165 source_db_repo,
1165 Reference(source_ref_type, source_ref_name, source_commit_id),
1166 Reference(source_ref_type, source_ref_name, source_commit_id),
1166 target_db_repo,
1167 target_db_repo,
1167 Reference(target_ref_type, target_ref_name, target_commit_id),
1168 Reference(target_ref_type, target_ref_name, target_commit_id),
1168 include_diff_info=False)
1169 include_diff_info=False)
1169
1170
1170 reviewers = validate_default_reviewers(_form['review_members'], reviewer_rules)
1171 reviewers = validate_default_reviewers(_form['review_members'], reviewer_rules)
1171 observers = validate_observers(_form['observer_members'], reviewer_rules)
1172 observers = validate_observers(_form['observer_members'], reviewer_rules)
1172
1173
1173 pullrequest_title = _form['pullrequest_title']
1174 pullrequest_title = _form['pullrequest_title']
1174 title_source_ref = source_ref.split(':', 2)[1]
1175 title_source_ref = source_ref.split(':', 2)[1]
1175 if not pullrequest_title:
1176 if not pullrequest_title:
1176 pullrequest_title = PullRequestModel().generate_pullrequest_title(
1177 pullrequest_title = PullRequestModel().generate_pullrequest_title(
1177 source=source_repo,
1178 source=source_repo,
1178 source_ref=title_source_ref,
1179 source_ref=title_source_ref,
1179 target=target_repo
1180 target=target_repo
1180 )
1181 )
1181
1182
1182 description = _form['pullrequest_desc']
1183 description = _form['pullrequest_desc']
1183 description_renderer = _form['description_renderer']
1184 description_renderer = _form['description_renderer']
1184
1185
1185 try:
1186 try:
1186 pull_request = PullRequestModel().create(
1187 pull_request = PullRequestModel().create(
1187 created_by=self._rhodecode_user.user_id,
1188 created_by=self._rhodecode_user.user_id,
1188 source_repo=source_repo,
1189 source_repo=source_repo,
1189 source_ref=source_ref,
1190 source_ref=source_ref,
1190 target_repo=target_repo,
1191 target_repo=target_repo,
1191 target_ref=target_ref,
1192 target_ref=target_ref,
1192 revisions=commit_ids,
1193 revisions=commit_ids,
1193 common_ancestor_id=common_ancestor_id,
1194 common_ancestor_id=common_ancestor_id,
1194 reviewers=reviewers,
1195 reviewers=reviewers,
1195 observers=observers,
1196 observers=observers,
1196 title=pullrequest_title,
1197 title=pullrequest_title,
1197 description=description,
1198 description=description,
1198 description_renderer=description_renderer,
1199 description_renderer=description_renderer,
1199 reviewer_data=reviewer_rules,
1200 reviewer_data=reviewer_rules,
1200 auth_user=self._rhodecode_user
1201 auth_user=self._rhodecode_user
1201 )
1202 )
1202 Session().commit()
1203 Session().commit()
1203
1204
1204 h.flash(_('Successfully opened new pull request'),
1205 h.flash(_('Successfully opened new pull request'),
1205 category='success')
1206 category='success')
1206 except Exception:
1207 except Exception:
1207 msg = _('Error occurred during creation of this pull request.')
1208 msg = _('Error occurred during creation of this pull request.')
1208 log.exception(msg)
1209 log.exception(msg)
1209 h.flash(msg, category='error')
1210 h.flash(msg, category='error')
1210
1211
1211 # copy the args back to redirect
1212 # copy the args back to redirect
1212 org_query = self.request.GET.mixed()
1213 org_query = self.request.GET.mixed()
1213 raise HTTPFound(
1214 raise HTTPFound(
1214 h.route_path('pullrequest_new', repo_name=self.db_repo_name,
1215 h.route_path('pullrequest_new', repo_name=self.db_repo_name,
1215 _query=org_query))
1216 _query=org_query))
1216
1217
1217 raise HTTPFound(
1218 raise HTTPFound(
1218 h.route_path('pullrequest_show', repo_name=target_repo,
1219 h.route_path('pullrequest_show', repo_name=target_repo,
1219 pull_request_id=pull_request.pull_request_id))
1220 pull_request_id=pull_request.pull_request_id))
1220
1221
1221 @LoginRequired()
1222 @LoginRequired()
1222 @NotAnonymous()
1223 @NotAnonymous()
1223 @HasRepoPermissionAnyDecorator(
1224 @HasRepoPermissionAnyDecorator(
1224 'repository.read', 'repository.write', 'repository.admin')
1225 'repository.read', 'repository.write', 'repository.admin')
1225 @CSRFRequired()
1226 @CSRFRequired()
1226 @view_config(
1227 @view_config(
1227 route_name='pullrequest_update', request_method='POST',
1228 route_name='pullrequest_update', request_method='POST',
1228 renderer='json_ext')
1229 renderer='json_ext')
1229 def pull_request_update(self):
1230 def pull_request_update(self):
1230 pull_request = PullRequest.get_or_404(
1231 pull_request = PullRequest.get_or_404(
1231 self.request.matchdict['pull_request_id'])
1232 self.request.matchdict['pull_request_id'])
1232 _ = self.request.translate
1233 _ = self.request.translate
1233
1234
1234 c = self.load_default_context()
1235 c = self.load_default_context()
1235 redirect_url = None
1236 redirect_url = None
1236
1237
1237 if pull_request.is_closed():
1238 if pull_request.is_closed():
1238 log.debug('update: forbidden because pull request is closed')
1239 log.debug('update: forbidden because pull request is closed')
1239 msg = _(u'Cannot update closed pull requests.')
1240 msg = _(u'Cannot update closed pull requests.')
1240 h.flash(msg, category='error')
1241 h.flash(msg, category='error')
1241 return {'response': True,
1242 return {'response': True,
1242 'redirect_url': redirect_url}
1243 'redirect_url': redirect_url}
1243
1244
1244 is_state_changing = pull_request.is_state_changing()
1245 is_state_changing = pull_request.is_state_changing()
1245 c.pr_broadcast_channel = channelstream.pr_channel(pull_request)
1246 c.pr_broadcast_channel = channelstream.pr_channel(pull_request)
1246
1247
1247 # only owner or admin can update it
1248 # only owner or admin can update it
1248 allowed_to_update = PullRequestModel().check_user_update(
1249 allowed_to_update = PullRequestModel().check_user_update(
1249 pull_request, self._rhodecode_user)
1250 pull_request, self._rhodecode_user)
1250
1251
1251 if allowed_to_update:
1252 if allowed_to_update:
1252 controls = peppercorn.parse(self.request.POST.items())
1253 controls = peppercorn.parse(self.request.POST.items())
1253 force_refresh = str2bool(self.request.POST.get('force_refresh'))
1254 force_refresh = str2bool(self.request.POST.get('force_refresh'))
1254
1255
1255 if 'review_members' in controls:
1256 if 'review_members' in controls:
1256 self._update_reviewers(
1257 self._update_reviewers(
1257 c,
1258 c,
1258 pull_request, controls['review_members'],
1259 pull_request, controls['review_members'],
1259 pull_request.reviewer_data,
1260 pull_request.reviewer_data,
1260 PullRequestReviewers.ROLE_REVIEWER)
1261 PullRequestReviewers.ROLE_REVIEWER)
1261 elif 'observer_members' in controls:
1262 elif 'observer_members' in controls:
1262 self._update_reviewers(
1263 self._update_reviewers(
1263 c,
1264 c,
1264 pull_request, controls['observer_members'],
1265 pull_request, controls['observer_members'],
1265 pull_request.reviewer_data,
1266 pull_request.reviewer_data,
1266 PullRequestReviewers.ROLE_OBSERVER)
1267 PullRequestReviewers.ROLE_OBSERVER)
1267 elif str2bool(self.request.POST.get('update_commits', 'false')):
1268 elif str2bool(self.request.POST.get('update_commits', 'false')):
1268 if is_state_changing:
1269 if is_state_changing:
1269 log.debug('commits update: forbidden because pull request is in state %s',
1270 log.debug('commits update: forbidden because pull request is in state %s',
1270 pull_request.pull_request_state)
1271 pull_request.pull_request_state)
1271 msg = _(u'Cannot update pull requests commits in state other than `{}`. '
1272 msg = _(u'Cannot update pull requests commits in state other than `{}`. '
1272 u'Current state is: `{}`').format(
1273 u'Current state is: `{}`').format(
1273 PullRequest.STATE_CREATED, pull_request.pull_request_state)
1274 PullRequest.STATE_CREATED, pull_request.pull_request_state)
1274 h.flash(msg, category='error')
1275 h.flash(msg, category='error')
1275 return {'response': True,
1276 return {'response': True,
1276 'redirect_url': redirect_url}
1277 'redirect_url': redirect_url}
1277
1278
1278 self._update_commits(c, pull_request)
1279 self._update_commits(c, pull_request)
1279 if force_refresh:
1280 if force_refresh:
1280 redirect_url = h.route_path(
1281 redirect_url = h.route_path(
1281 'pullrequest_show', repo_name=self.db_repo_name,
1282 'pullrequest_show', repo_name=self.db_repo_name,
1282 pull_request_id=pull_request.pull_request_id,
1283 pull_request_id=pull_request.pull_request_id,
1283 _query={"force_refresh": 1})
1284 _query={"force_refresh": 1})
1284 elif str2bool(self.request.POST.get('edit_pull_request', 'false')):
1285 elif str2bool(self.request.POST.get('edit_pull_request', 'false')):
1285 self._edit_pull_request(pull_request)
1286 self._edit_pull_request(pull_request)
1286 else:
1287 else:
1287 log.error('Unhandled update data.')
1288 log.error('Unhandled update data.')
1288 raise HTTPBadRequest()
1289 raise HTTPBadRequest()
1289
1290
1290 return {'response': True,
1291 return {'response': True,
1291 'redirect_url': redirect_url}
1292 'redirect_url': redirect_url}
1292 raise HTTPForbidden()
1293 raise HTTPForbidden()
1293
1294
1294 def _edit_pull_request(self, pull_request):
1295 def _edit_pull_request(self, pull_request):
1295 """
1296 """
1296 Edit title and description
1297 Edit title and description
1297 """
1298 """
1298 _ = self.request.translate
1299 _ = self.request.translate
1299
1300
1300 try:
1301 try:
1301 PullRequestModel().edit(
1302 PullRequestModel().edit(
1302 pull_request,
1303 pull_request,
1303 self.request.POST.get('title'),
1304 self.request.POST.get('title'),
1304 self.request.POST.get('description'),
1305 self.request.POST.get('description'),
1305 self.request.POST.get('description_renderer'),
1306 self.request.POST.get('description_renderer'),
1306 self._rhodecode_user)
1307 self._rhodecode_user)
1307 except ValueError:
1308 except ValueError:
1308 msg = _(u'Cannot update closed pull requests.')
1309 msg = _(u'Cannot update closed pull requests.')
1309 h.flash(msg, category='error')
1310 h.flash(msg, category='error')
1310 return
1311 return
1311 else:
1312 else:
1312 Session().commit()
1313 Session().commit()
1313
1314
1314 msg = _(u'Pull request title & description updated.')
1315 msg = _(u'Pull request title & description updated.')
1315 h.flash(msg, category='success')
1316 h.flash(msg, category='success')
1316 return
1317 return
1317
1318
1318 def _update_commits(self, c, pull_request):
1319 def _update_commits(self, c, pull_request):
1319 _ = self.request.translate
1320 _ = self.request.translate
1320
1321
1321 with pull_request.set_state(PullRequest.STATE_UPDATING):
1322 with pull_request.set_state(PullRequest.STATE_UPDATING):
1322 resp = PullRequestModel().update_commits(
1323 resp = PullRequestModel().update_commits(
1323 pull_request, self._rhodecode_db_user)
1324 pull_request, self._rhodecode_db_user)
1324
1325
1325 if resp.executed:
1326 if resp.executed:
1326
1327
1327 if resp.target_changed and resp.source_changed:
1328 if resp.target_changed and resp.source_changed:
1328 changed = 'target and source repositories'
1329 changed = 'target and source repositories'
1329 elif resp.target_changed and not resp.source_changed:
1330 elif resp.target_changed and not resp.source_changed:
1330 changed = 'target repository'
1331 changed = 'target repository'
1331 elif not resp.target_changed and resp.source_changed:
1332 elif not resp.target_changed and resp.source_changed:
1332 changed = 'source repository'
1333 changed = 'source repository'
1333 else:
1334 else:
1334 changed = 'nothing'
1335 changed = 'nothing'
1335
1336
1336 msg = _(u'Pull request updated to "{source_commit_id}" with '
1337 msg = _(u'Pull request updated to "{source_commit_id}" with '
1337 u'{count_added} added, {count_removed} removed commits. '
1338 u'{count_added} added, {count_removed} removed commits. '
1338 u'Source of changes: {change_source}.')
1339 u'Source of changes: {change_source}.')
1339 msg = msg.format(
1340 msg = msg.format(
1340 source_commit_id=pull_request.source_ref_parts.commit_id,
1341 source_commit_id=pull_request.source_ref_parts.commit_id,
1341 count_added=len(resp.changes.added),
1342 count_added=len(resp.changes.added),
1342 count_removed=len(resp.changes.removed),
1343 count_removed=len(resp.changes.removed),
1343 change_source=changed)
1344 change_source=changed)
1344 h.flash(msg, category='success')
1345 h.flash(msg, category='success')
1345 channelstream.pr_update_channelstream_push(
1346 channelstream.pr_update_channelstream_push(
1346 self.request, c.pr_broadcast_channel, self._rhodecode_user, msg)
1347 self.request, c.pr_broadcast_channel, self._rhodecode_user, msg)
1347 else:
1348 else:
1348 msg = PullRequestModel.UPDATE_STATUS_MESSAGES[resp.reason]
1349 msg = PullRequestModel.UPDATE_STATUS_MESSAGES[resp.reason]
1349 warning_reasons = [
1350 warning_reasons = [
1350 UpdateFailureReason.NO_CHANGE,
1351 UpdateFailureReason.NO_CHANGE,
1351 UpdateFailureReason.WRONG_REF_TYPE,
1352 UpdateFailureReason.WRONG_REF_TYPE,
1352 ]
1353 ]
1353 category = 'warning' if resp.reason in warning_reasons else 'error'
1354 category = 'warning' if resp.reason in warning_reasons else 'error'
1354 h.flash(msg, category=category)
1355 h.flash(msg, category=category)
1355
1356
1356 def _update_reviewers(self, c, pull_request, review_members, reviewer_rules, role):
1357 def _update_reviewers(self, c, pull_request, review_members, reviewer_rules, role):
1357 _ = self.request.translate
1358 _ = self.request.translate
1358
1359
1359 get_default_reviewers_data, validate_default_reviewers, validate_observers = \
1360 get_default_reviewers_data, validate_default_reviewers, validate_observers = \
1360 PullRequestModel().get_reviewer_functions()
1361 PullRequestModel().get_reviewer_functions()
1361
1362
1362 if role == PullRequestReviewers.ROLE_REVIEWER:
1363 if role == PullRequestReviewers.ROLE_REVIEWER:
1363 try:
1364 try:
1364 reviewers = validate_default_reviewers(review_members, reviewer_rules)
1365 reviewers = validate_default_reviewers(review_members, reviewer_rules)
1365 except ValueError as e:
1366 except ValueError as e:
1366 log.error('Reviewers Validation: {}'.format(e))
1367 log.error('Reviewers Validation: {}'.format(e))
1367 h.flash(e, category='error')
1368 h.flash(e, category='error')
1368 return
1369 return
1369
1370
1370 old_calculated_status = pull_request.calculated_review_status()
1371 old_calculated_status = pull_request.calculated_review_status()
1371 PullRequestModel().update_reviewers(
1372 PullRequestModel().update_reviewers(
1372 pull_request, reviewers, self._rhodecode_user)
1373 pull_request, reviewers, self._rhodecode_user)
1373
1374
1374 Session().commit()
1375 Session().commit()
1375
1376
1376 msg = _('Pull request reviewers updated.')
1377 msg = _('Pull request reviewers updated.')
1377 h.flash(msg, category='success')
1378 h.flash(msg, category='success')
1378 channelstream.pr_update_channelstream_push(
1379 channelstream.pr_update_channelstream_push(
1379 self.request, c.pr_broadcast_channel, self._rhodecode_user, msg)
1380 self.request, c.pr_broadcast_channel, self._rhodecode_user, msg)
1380
1381
1381 # trigger status changed if change in reviewers changes the status
1382 # trigger status changed if change in reviewers changes the status
1382 calculated_status = pull_request.calculated_review_status()
1383 calculated_status = pull_request.calculated_review_status()
1383 if old_calculated_status != calculated_status:
1384 if old_calculated_status != calculated_status:
1384 PullRequestModel().trigger_pull_request_hook(
1385 PullRequestModel().trigger_pull_request_hook(
1385 pull_request, self._rhodecode_user, 'review_status_change',
1386 pull_request, self._rhodecode_user, 'review_status_change',
1386 data={'status': calculated_status})
1387 data={'status': calculated_status})
1387
1388
1388 elif role == PullRequestReviewers.ROLE_OBSERVER:
1389 elif role == PullRequestReviewers.ROLE_OBSERVER:
1389 try:
1390 try:
1390 observers = validate_observers(review_members, reviewer_rules)
1391 observers = validate_observers(review_members, reviewer_rules)
1391 except ValueError as e:
1392 except ValueError as e:
1392 log.error('Observers Validation: {}'.format(e))
1393 log.error('Observers Validation: {}'.format(e))
1393 h.flash(e, category='error')
1394 h.flash(e, category='error')
1394 return
1395 return
1395
1396
1396 PullRequestModel().update_observers(
1397 PullRequestModel().update_observers(
1397 pull_request, observers, self._rhodecode_user)
1398 pull_request, observers, self._rhodecode_user)
1398
1399
1399 Session().commit()
1400 Session().commit()
1400 msg = _('Pull request observers updated.')
1401 msg = _('Pull request observers updated.')
1401 h.flash(msg, category='success')
1402 h.flash(msg, category='success')
1402 channelstream.pr_update_channelstream_push(
1403 channelstream.pr_update_channelstream_push(
1403 self.request, c.pr_broadcast_channel, self._rhodecode_user, msg)
1404 self.request, c.pr_broadcast_channel, self._rhodecode_user, msg)
1404
1405
1405 @LoginRequired()
1406 @LoginRequired()
1406 @NotAnonymous()
1407 @NotAnonymous()
1407 @HasRepoPermissionAnyDecorator(
1408 @HasRepoPermissionAnyDecorator(
1408 'repository.read', 'repository.write', 'repository.admin')
1409 'repository.read', 'repository.write', 'repository.admin')
1409 @CSRFRequired()
1410 @CSRFRequired()
1410 @view_config(
1411 @view_config(
1411 route_name='pullrequest_merge', request_method='POST',
1412 route_name='pullrequest_merge', request_method='POST',
1412 renderer='json_ext')
1413 renderer='json_ext')
1413 def pull_request_merge(self):
1414 def pull_request_merge(self):
1414 """
1415 """
1415 Merge will perform a server-side merge of the specified
1416 Merge will perform a server-side merge of the specified
1416 pull request, if the pull request is approved and mergeable.
1417 pull request, if the pull request is approved and mergeable.
1417 After successful merging, the pull request is automatically
1418 After successful merging, the pull request is automatically
1418 closed, with a relevant comment.
1419 closed, with a relevant comment.
1419 """
1420 """
1420 pull_request = PullRequest.get_or_404(
1421 pull_request = PullRequest.get_or_404(
1421 self.request.matchdict['pull_request_id'])
1422 self.request.matchdict['pull_request_id'])
1422 _ = self.request.translate
1423 _ = self.request.translate
1423
1424
1424 if pull_request.is_state_changing():
1425 if pull_request.is_state_changing():
1425 log.debug('show: forbidden because pull request is in state %s',
1426 log.debug('show: forbidden because pull request is in state %s',
1426 pull_request.pull_request_state)
1427 pull_request.pull_request_state)
1427 msg = _(u'Cannot merge pull requests in state other than `{}`. '
1428 msg = _(u'Cannot merge pull requests in state other than `{}`. '
1428 u'Current state is: `{}`').format(PullRequest.STATE_CREATED,
1429 u'Current state is: `{}`').format(PullRequest.STATE_CREATED,
1429 pull_request.pull_request_state)
1430 pull_request.pull_request_state)
1430 h.flash(msg, category='error')
1431 h.flash(msg, category='error')
1431 raise HTTPFound(
1432 raise HTTPFound(
1432 h.route_path('pullrequest_show',
1433 h.route_path('pullrequest_show',
1433 repo_name=pull_request.target_repo.repo_name,
1434 repo_name=pull_request.target_repo.repo_name,
1434 pull_request_id=pull_request.pull_request_id))
1435 pull_request_id=pull_request.pull_request_id))
1435
1436
1436 self.load_default_context()
1437 self.load_default_context()
1437
1438
1438 with pull_request.set_state(PullRequest.STATE_UPDATING):
1439 with pull_request.set_state(PullRequest.STATE_UPDATING):
1439 check = MergeCheck.validate(
1440 check = MergeCheck.validate(
1440 pull_request, auth_user=self._rhodecode_user,
1441 pull_request, auth_user=self._rhodecode_user,
1441 translator=self.request.translate)
1442 translator=self.request.translate)
1442 merge_possible = not check.failed
1443 merge_possible = not check.failed
1443
1444
1444 for err_type, error_msg in check.errors:
1445 for err_type, error_msg in check.errors:
1445 h.flash(error_msg, category=err_type)
1446 h.flash(error_msg, category=err_type)
1446
1447
1447 if merge_possible:
1448 if merge_possible:
1448 log.debug("Pre-conditions checked, trying to merge.")
1449 log.debug("Pre-conditions checked, trying to merge.")
1449 extras = vcs_operation_context(
1450 extras = vcs_operation_context(
1450 self.request.environ, repo_name=pull_request.target_repo.repo_name,
1451 self.request.environ, repo_name=pull_request.target_repo.repo_name,
1451 username=self._rhodecode_db_user.username, action='push',
1452 username=self._rhodecode_db_user.username, action='push',
1452 scm=pull_request.target_repo.repo_type)
1453 scm=pull_request.target_repo.repo_type)
1453 with pull_request.set_state(PullRequest.STATE_UPDATING):
1454 with pull_request.set_state(PullRequest.STATE_UPDATING):
1454 self._merge_pull_request(
1455 self._merge_pull_request(
1455 pull_request, self._rhodecode_db_user, extras)
1456 pull_request, self._rhodecode_db_user, extras)
1456 else:
1457 else:
1457 log.debug("Pre-conditions failed, NOT merging.")
1458 log.debug("Pre-conditions failed, NOT merging.")
1458
1459
1459 raise HTTPFound(
1460 raise HTTPFound(
1460 h.route_path('pullrequest_show',
1461 h.route_path('pullrequest_show',
1461 repo_name=pull_request.target_repo.repo_name,
1462 repo_name=pull_request.target_repo.repo_name,
1462 pull_request_id=pull_request.pull_request_id))
1463 pull_request_id=pull_request.pull_request_id))
1463
1464
1464 def _merge_pull_request(self, pull_request, user, extras):
1465 def _merge_pull_request(self, pull_request, user, extras):
1465 _ = self.request.translate
1466 _ = self.request.translate
1466 merge_resp = PullRequestModel().merge_repo(pull_request, user, extras=extras)
1467 merge_resp = PullRequestModel().merge_repo(pull_request, user, extras=extras)
1467
1468
1468 if merge_resp.executed:
1469 if merge_resp.executed:
1469 log.debug("The merge was successful, closing the pull request.")
1470 log.debug("The merge was successful, closing the pull request.")
1470 PullRequestModel().close_pull_request(
1471 PullRequestModel().close_pull_request(
1471 pull_request.pull_request_id, user)
1472 pull_request.pull_request_id, user)
1472 Session().commit()
1473 Session().commit()
1473 msg = _('Pull request was successfully merged and closed.')
1474 msg = _('Pull request was successfully merged and closed.')
1474 h.flash(msg, category='success')
1475 h.flash(msg, category='success')
1475 else:
1476 else:
1476 log.debug(
1477 log.debug(
1477 "The merge was not successful. Merge response: %s", merge_resp)
1478 "The merge was not successful. Merge response: %s", merge_resp)
1478 msg = merge_resp.merge_status_message
1479 msg = merge_resp.merge_status_message
1479 h.flash(msg, category='error')
1480 h.flash(msg, category='error')
1480
1481
1481 @LoginRequired()
1482 @LoginRequired()
1482 @NotAnonymous()
1483 @NotAnonymous()
1483 @HasRepoPermissionAnyDecorator(
1484 @HasRepoPermissionAnyDecorator(
1484 'repository.read', 'repository.write', 'repository.admin')
1485 'repository.read', 'repository.write', 'repository.admin')
1485 @CSRFRequired()
1486 @CSRFRequired()
1486 @view_config(
1487 @view_config(
1487 route_name='pullrequest_delete', request_method='POST',
1488 route_name='pullrequest_delete', request_method='POST',
1488 renderer='json_ext')
1489 renderer='json_ext')
1489 def pull_request_delete(self):
1490 def pull_request_delete(self):
1490 _ = self.request.translate
1491 _ = self.request.translate
1491
1492
1492 pull_request = PullRequest.get_or_404(
1493 pull_request = PullRequest.get_or_404(
1493 self.request.matchdict['pull_request_id'])
1494 self.request.matchdict['pull_request_id'])
1494 self.load_default_context()
1495 self.load_default_context()
1495
1496
1496 pr_closed = pull_request.is_closed()
1497 pr_closed = pull_request.is_closed()
1497 allowed_to_delete = PullRequestModel().check_user_delete(
1498 allowed_to_delete = PullRequestModel().check_user_delete(
1498 pull_request, self._rhodecode_user) and not pr_closed
1499 pull_request, self._rhodecode_user) and not pr_closed
1499
1500
1500 # only owner can delete it !
1501 # only owner can delete it !
1501 if allowed_to_delete:
1502 if allowed_to_delete:
1502 PullRequestModel().delete(pull_request, self._rhodecode_user)
1503 PullRequestModel().delete(pull_request, self._rhodecode_user)
1503 Session().commit()
1504 Session().commit()
1504 h.flash(_('Successfully deleted pull request'),
1505 h.flash(_('Successfully deleted pull request'),
1505 category='success')
1506 category='success')
1506 raise HTTPFound(h.route_path('pullrequest_show_all',
1507 raise HTTPFound(h.route_path('pullrequest_show_all',
1507 repo_name=self.db_repo_name))
1508 repo_name=self.db_repo_name))
1508
1509
1509 log.warning('user %s tried to delete pull request without access',
1510 log.warning('user %s tried to delete pull request without access',
1510 self._rhodecode_user)
1511 self._rhodecode_user)
1511 raise HTTPNotFound()
1512 raise HTTPNotFound()
1512
1513
1513 @LoginRequired()
1514 @LoginRequired()
1514 @NotAnonymous()
1515 @NotAnonymous()
1515 @HasRepoPermissionAnyDecorator(
1516 @HasRepoPermissionAnyDecorator(
1516 'repository.read', 'repository.write', 'repository.admin')
1517 'repository.read', 'repository.write', 'repository.admin')
1517 @CSRFRequired()
1518 @CSRFRequired()
1518 @view_config(
1519 @view_config(
1519 route_name='pullrequest_comment_create', request_method='POST',
1520 route_name='pullrequest_comment_create', request_method='POST',
1520 renderer='json_ext')
1521 renderer='json_ext')
1521 def pull_request_comment_create(self):
1522 def pull_request_comment_create(self):
1522 _ = self.request.translate
1523 _ = self.request.translate
1523
1524
1524 pull_request = PullRequest.get_or_404(
1525 pull_request = PullRequest.get_or_404(
1525 self.request.matchdict['pull_request_id'])
1526 self.request.matchdict['pull_request_id'])
1526 pull_request_id = pull_request.pull_request_id
1527 pull_request_id = pull_request.pull_request_id
1527
1528
1528 if pull_request.is_closed():
1529 if pull_request.is_closed():
1529 log.debug('comment: forbidden because pull request is closed')
1530 log.debug('comment: forbidden because pull request is closed')
1530 raise HTTPForbidden()
1531 raise HTTPForbidden()
1531
1532
1532 allowed_to_comment = PullRequestModel().check_user_comment(
1533 allowed_to_comment = PullRequestModel().check_user_comment(
1533 pull_request, self._rhodecode_user)
1534 pull_request, self._rhodecode_user)
1534 if not allowed_to_comment:
1535 if not allowed_to_comment:
1535 log.debug('comment: forbidden because pull request is from forbidden repo')
1536 log.debug('comment: forbidden because pull request is from forbidden repo')
1536 raise HTTPForbidden()
1537 raise HTTPForbidden()
1537
1538
1538 c = self.load_default_context()
1539 c = self.load_default_context()
1539
1540
1540 status = self.request.POST.get('changeset_status', None)
1541 status = self.request.POST.get('changeset_status', None)
1541 text = self.request.POST.get('text')
1542 text = self.request.POST.get('text')
1542 comment_type = self.request.POST.get('comment_type')
1543 comment_type = self.request.POST.get('comment_type')
1543 resolves_comment_id = self.request.POST.get('resolves_comment_id', None)
1544 resolves_comment_id = self.request.POST.get('resolves_comment_id', None)
1544 close_pull_request = self.request.POST.get('close_pull_request')
1545 close_pull_request = self.request.POST.get('close_pull_request')
1545
1546
1546 # the logic here should work like following, if we submit close
1547 # the logic here should work like following, if we submit close
1547 # pr comment, use `close_pull_request_with_comment` function
1548 # pr comment, use `close_pull_request_with_comment` function
1548 # else handle regular comment logic
1549 # else handle regular comment logic
1549
1550
1550 if close_pull_request:
1551 if close_pull_request:
1551 # only owner or admin or person with write permissions
1552 # only owner or admin or person with write permissions
1552 allowed_to_close = PullRequestModel().check_user_update(
1553 allowed_to_close = PullRequestModel().check_user_update(
1553 pull_request, self._rhodecode_user)
1554 pull_request, self._rhodecode_user)
1554 if not allowed_to_close:
1555 if not allowed_to_close:
1555 log.debug('comment: forbidden because not allowed to close '
1556 log.debug('comment: forbidden because not allowed to close '
1556 'pull request %s', pull_request_id)
1557 'pull request %s', pull_request_id)
1557 raise HTTPForbidden()
1558 raise HTTPForbidden()
1558
1559
1559 # This also triggers `review_status_change`
1560 # This also triggers `review_status_change`
1560 comment, status = PullRequestModel().close_pull_request_with_comment(
1561 comment, status = PullRequestModel().close_pull_request_with_comment(
1561 pull_request, self._rhodecode_user, self.db_repo, message=text,
1562 pull_request, self._rhodecode_user, self.db_repo, message=text,
1562 auth_user=self._rhodecode_user)
1563 auth_user=self._rhodecode_user)
1563 Session().flush()
1564 Session().flush()
1564
1565
1565 PullRequestModel().trigger_pull_request_hook(
1566 PullRequestModel().trigger_pull_request_hook(
1566 pull_request, self._rhodecode_user, 'comment',
1567 pull_request, self._rhodecode_user, 'comment',
1567 data={'comment': comment})
1568 data={'comment': comment})
1568
1569
1569 else:
1570 else:
1570 # regular comment case, could be inline, or one with status.
1571 # regular comment case, could be inline, or one with status.
1571 # for that one we check also permissions
1572 # for that one we check also permissions
1572
1573
1573 allowed_to_change_status = PullRequestModel().check_user_change_status(
1574 allowed_to_change_status = PullRequestModel().check_user_change_status(
1574 pull_request, self._rhodecode_user)
1575 pull_request, self._rhodecode_user)
1575
1576
1576 if status and allowed_to_change_status:
1577 if status and allowed_to_change_status:
1577 message = (_('Status change %(transition_icon)s %(status)s')
1578 message = (_('Status change %(transition_icon)s %(status)s')
1578 % {'transition_icon': '>',
1579 % {'transition_icon': '>',
1579 'status': ChangesetStatus.get_status_lbl(status)})
1580 'status': ChangesetStatus.get_status_lbl(status)})
1580 text = text or message
1581 text = text or message
1581
1582
1582 comment = CommentsModel().create(
1583 comment = CommentsModel().create(
1583 text=text,
1584 text=text,
1584 repo=self.db_repo.repo_id,
1585 repo=self.db_repo.repo_id,
1585 user=self._rhodecode_user.user_id,
1586 user=self._rhodecode_user.user_id,
1586 pull_request=pull_request,
1587 pull_request=pull_request,
1587 f_path=self.request.POST.get('f_path'),
1588 f_path=self.request.POST.get('f_path'),
1588 line_no=self.request.POST.get('line'),
1589 line_no=self.request.POST.get('line'),
1589 status_change=(ChangesetStatus.get_status_lbl(status)
1590 status_change=(ChangesetStatus.get_status_lbl(status)
1590 if status and allowed_to_change_status else None),
1591 if status and allowed_to_change_status else None),
1591 status_change_type=(status
1592 status_change_type=(status
1592 if status and allowed_to_change_status else None),
1593 if status and allowed_to_change_status else None),
1593 comment_type=comment_type,
1594 comment_type=comment_type,
1594 resolves_comment_id=resolves_comment_id,
1595 resolves_comment_id=resolves_comment_id,
1595 auth_user=self._rhodecode_user
1596 auth_user=self._rhodecode_user
1596 )
1597 )
1597 is_inline = bool(comment.f_path and comment.line_no)
1598 is_inline = bool(comment.f_path and comment.line_no)
1598
1599
1599 if allowed_to_change_status:
1600 if allowed_to_change_status:
1600 # calculate old status before we change it
1601 # calculate old status before we change it
1601 old_calculated_status = pull_request.calculated_review_status()
1602 old_calculated_status = pull_request.calculated_review_status()
1602
1603
1603 # get status if set !
1604 # get status if set !
1604 if status:
1605 if status:
1605 ChangesetStatusModel().set_status(
1606 ChangesetStatusModel().set_status(
1606 self.db_repo.repo_id,
1607 self.db_repo.repo_id,
1607 status,
1608 status,
1608 self._rhodecode_user.user_id,
1609 self._rhodecode_user.user_id,
1609 comment,
1610 comment,
1610 pull_request=pull_request
1611 pull_request=pull_request
1611 )
1612 )
1612
1613
1613 Session().flush()
1614 Session().flush()
1614 # this is somehow required to get access to some relationship
1615 # this is somehow required to get access to some relationship
1615 # loaded on comment
1616 # loaded on comment
1616 Session().refresh(comment)
1617 Session().refresh(comment)
1617
1618
1618 PullRequestModel().trigger_pull_request_hook(
1619 PullRequestModel().trigger_pull_request_hook(
1619 pull_request, self._rhodecode_user, 'comment',
1620 pull_request, self._rhodecode_user, 'comment',
1620 data={'comment': comment})
1621 data={'comment': comment})
1621
1622
1622 # we now calculate the status of pull request, and based on that
1623 # we now calculate the status of pull request, and based on that
1623 # calculation we set the commits status
1624 # calculation we set the commits status
1624 calculated_status = pull_request.calculated_review_status()
1625 calculated_status = pull_request.calculated_review_status()
1625 if old_calculated_status != calculated_status:
1626 if old_calculated_status != calculated_status:
1626 PullRequestModel().trigger_pull_request_hook(
1627 PullRequestModel().trigger_pull_request_hook(
1627 pull_request, self._rhodecode_user, 'review_status_change',
1628 pull_request, self._rhodecode_user, 'review_status_change',
1628 data={'status': calculated_status})
1629 data={'status': calculated_status})
1629
1630
1630 Session().commit()
1631 Session().commit()
1631
1632
1632 data = {
1633 data = {
1633 'target_id': h.safeid(h.safe_unicode(
1634 'target_id': h.safeid(h.safe_unicode(
1634 self.request.POST.get('f_path'))),
1635 self.request.POST.get('f_path'))),
1635 }
1636 }
1636 if comment:
1637 if comment:
1637 c.co = comment
1638 c.co = comment
1638 c.at_version_num = None
1639 c.at_version_num = None
1639 rendered_comment = render(
1640 rendered_comment = render(
1640 'rhodecode:templates/changeset/changeset_comment_block.mako',
1641 'rhodecode:templates/changeset/changeset_comment_block.mako',
1641 self._get_template_context(c), self.request)
1642 self._get_template_context(c), self.request)
1642
1643
1643 data.update(comment.get_dict())
1644 data.update(comment.get_dict())
1644 data.update({'rendered_text': rendered_comment})
1645 data.update({'rendered_text': rendered_comment})
1645
1646
1646 comment_broadcast_channel = channelstream.comment_channel(
1647 comment_broadcast_channel = channelstream.comment_channel(
1647 self.db_repo_name, pull_request_obj=pull_request)
1648 self.db_repo_name, pull_request_obj=pull_request)
1648
1649
1649 comment_data = data
1650 comment_data = data
1650 comment_type = 'inline' if is_inline else 'general'
1651 comment_type = 'inline' if is_inline else 'general'
1651 channelstream.comment_channelstream_push(
1652 channelstream.comment_channelstream_push(
1652 self.request, comment_broadcast_channel, self._rhodecode_user,
1653 self.request, comment_broadcast_channel, self._rhodecode_user,
1653 _('posted a new {} comment').format(comment_type),
1654 _('posted a new {} comment').format(comment_type),
1654 comment_data=comment_data)
1655 comment_data=comment_data)
1655
1656
1656 return data
1657 return data
1657
1658
1658 @LoginRequired()
1659 @LoginRequired()
1659 @NotAnonymous()
1660 @NotAnonymous()
1660 @HasRepoPermissionAnyDecorator(
1661 @HasRepoPermissionAnyDecorator(
1661 'repository.read', 'repository.write', 'repository.admin')
1662 'repository.read', 'repository.write', 'repository.admin')
1662 @CSRFRequired()
1663 @CSRFRequired()
1663 @view_config(
1664 @view_config(
1664 route_name='pullrequest_comment_delete', request_method='POST',
1665 route_name='pullrequest_comment_delete', request_method='POST',
1665 renderer='json_ext')
1666 renderer='json_ext')
1666 def pull_request_comment_delete(self):
1667 def pull_request_comment_delete(self):
1667 pull_request = PullRequest.get_or_404(
1668 pull_request = PullRequest.get_or_404(
1668 self.request.matchdict['pull_request_id'])
1669 self.request.matchdict['pull_request_id'])
1669
1670
1670 comment = ChangesetComment.get_or_404(
1671 comment = ChangesetComment.get_or_404(
1671 self.request.matchdict['comment_id'])
1672 self.request.matchdict['comment_id'])
1672 comment_id = comment.comment_id
1673 comment_id = comment.comment_id
1673
1674
1674 if comment.immutable:
1675 if comment.immutable:
1675 # don't allow deleting comments that are immutable
1676 # don't allow deleting comments that are immutable
1676 raise HTTPForbidden()
1677 raise HTTPForbidden()
1677
1678
1678 if pull_request.is_closed():
1679 if pull_request.is_closed():
1679 log.debug('comment: forbidden because pull request is closed')
1680 log.debug('comment: forbidden because pull request is closed')
1680 raise HTTPForbidden()
1681 raise HTTPForbidden()
1681
1682
1682 if not comment:
1683 if not comment:
1683 log.debug('Comment with id:%s not found, skipping', comment_id)
1684 log.debug('Comment with id:%s not found, skipping', comment_id)
1684 # comment already deleted in another call probably
1685 # comment already deleted in another call probably
1685 return True
1686 return True
1686
1687
1687 if comment.pull_request.is_closed():
1688 if comment.pull_request.is_closed():
1688 # don't allow deleting comments on closed pull request
1689 # don't allow deleting comments on closed pull request
1689 raise HTTPForbidden()
1690 raise HTTPForbidden()
1690
1691
1691 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
1692 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
1692 super_admin = h.HasPermissionAny('hg.admin')()
1693 super_admin = h.HasPermissionAny('hg.admin')()
1693 comment_owner = comment.author.user_id == self._rhodecode_user.user_id
1694 comment_owner = comment.author.user_id == self._rhodecode_user.user_id
1694 is_repo_comment = comment.repo.repo_name == self.db_repo_name
1695 is_repo_comment = comment.repo.repo_name == self.db_repo_name
1695 comment_repo_admin = is_repo_admin and is_repo_comment
1696 comment_repo_admin = is_repo_admin and is_repo_comment
1696
1697
1697 if super_admin or comment_owner or comment_repo_admin:
1698 if super_admin or comment_owner or comment_repo_admin:
1698 old_calculated_status = comment.pull_request.calculated_review_status()
1699 old_calculated_status = comment.pull_request.calculated_review_status()
1699 CommentsModel().delete(comment=comment, auth_user=self._rhodecode_user)
1700 CommentsModel().delete(comment=comment, auth_user=self._rhodecode_user)
1700 Session().commit()
1701 Session().commit()
1701 calculated_status = comment.pull_request.calculated_review_status()
1702 calculated_status = comment.pull_request.calculated_review_status()
1702 if old_calculated_status != calculated_status:
1703 if old_calculated_status != calculated_status:
1703 PullRequestModel().trigger_pull_request_hook(
1704 PullRequestModel().trigger_pull_request_hook(
1704 comment.pull_request, self._rhodecode_user, 'review_status_change',
1705 comment.pull_request, self._rhodecode_user, 'review_status_change',
1705 data={'status': calculated_status})
1706 data={'status': calculated_status})
1706 return True
1707 return True
1707 else:
1708 else:
1708 log.warning('No permissions for user %s to delete comment_id: %s',
1709 log.warning('No permissions for user %s to delete comment_id: %s',
1709 self._rhodecode_db_user, comment_id)
1710 self._rhodecode_db_user, comment_id)
1710 raise HTTPNotFound()
1711 raise HTTPNotFound()
1711
1712
1712 @LoginRequired()
1713 @LoginRequired()
1713 @NotAnonymous()
1714 @NotAnonymous()
1714 @HasRepoPermissionAnyDecorator(
1715 @HasRepoPermissionAnyDecorator(
1715 'repository.read', 'repository.write', 'repository.admin')
1716 'repository.read', 'repository.write', 'repository.admin')
1716 @CSRFRequired()
1717 @CSRFRequired()
1717 @view_config(
1718 @view_config(
1718 route_name='pullrequest_comment_edit', request_method='POST',
1719 route_name='pullrequest_comment_edit', request_method='POST',
1719 renderer='json_ext')
1720 renderer='json_ext')
1720 def pull_request_comment_edit(self):
1721 def pull_request_comment_edit(self):
1721 self.load_default_context()
1722 self.load_default_context()
1722
1723
1723 pull_request = PullRequest.get_or_404(
1724 pull_request = PullRequest.get_or_404(
1724 self.request.matchdict['pull_request_id']
1725 self.request.matchdict['pull_request_id']
1725 )
1726 )
1726 comment = ChangesetComment.get_or_404(
1727 comment = ChangesetComment.get_or_404(
1727 self.request.matchdict['comment_id']
1728 self.request.matchdict['comment_id']
1728 )
1729 )
1729 comment_id = comment.comment_id
1730 comment_id = comment.comment_id
1730
1731
1731 if comment.immutable:
1732 if comment.immutable:
1732 # don't allow deleting comments that are immutable
1733 # don't allow deleting comments that are immutable
1733 raise HTTPForbidden()
1734 raise HTTPForbidden()
1734
1735
1735 if pull_request.is_closed():
1736 if pull_request.is_closed():
1736 log.debug('comment: forbidden because pull request is closed')
1737 log.debug('comment: forbidden because pull request is closed')
1737 raise HTTPForbidden()
1738 raise HTTPForbidden()
1738
1739
1739 if not comment:
1740 if not comment:
1740 log.debug('Comment with id:%s not found, skipping', comment_id)
1741 log.debug('Comment with id:%s not found, skipping', comment_id)
1741 # comment already deleted in another call probably
1742 # comment already deleted in another call probably
1742 return True
1743 return True
1743
1744
1744 if comment.pull_request.is_closed():
1745 if comment.pull_request.is_closed():
1745 # don't allow deleting comments on closed pull request
1746 # don't allow deleting comments on closed pull request
1746 raise HTTPForbidden()
1747 raise HTTPForbidden()
1747
1748
1748 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
1749 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
1749 super_admin = h.HasPermissionAny('hg.admin')()
1750 super_admin = h.HasPermissionAny('hg.admin')()
1750 comment_owner = comment.author.user_id == self._rhodecode_user.user_id
1751 comment_owner = comment.author.user_id == self._rhodecode_user.user_id
1751 is_repo_comment = comment.repo.repo_name == self.db_repo_name
1752 is_repo_comment = comment.repo.repo_name == self.db_repo_name
1752 comment_repo_admin = is_repo_admin and is_repo_comment
1753 comment_repo_admin = is_repo_admin and is_repo_comment
1753
1754
1754 if super_admin or comment_owner or comment_repo_admin:
1755 if super_admin or comment_owner or comment_repo_admin:
1755 text = self.request.POST.get('text')
1756 text = self.request.POST.get('text')
1756 version = self.request.POST.get('version')
1757 version = self.request.POST.get('version')
1757 if text == comment.text:
1758 if text == comment.text:
1758 log.warning(
1759 log.warning(
1759 'Comment(PR): '
1760 'Comment(PR): '
1760 'Trying to create new version '
1761 'Trying to create new version '
1761 'with the same comment body {}'.format(
1762 'with the same comment body {}'.format(
1762 comment_id,
1763 comment_id,
1763 )
1764 )
1764 )
1765 )
1765 raise HTTPNotFound()
1766 raise HTTPNotFound()
1766
1767
1767 if version.isdigit():
1768 if version.isdigit():
1768 version = int(version)
1769 version = int(version)
1769 else:
1770 else:
1770 log.warning(
1771 log.warning(
1771 'Comment(PR): Wrong version type {} {} '
1772 'Comment(PR): Wrong version type {} {} '
1772 'for comment {}'.format(
1773 'for comment {}'.format(
1773 version,
1774 version,
1774 type(version),
1775 type(version),
1775 comment_id,
1776 comment_id,
1776 )
1777 )
1777 )
1778 )
1778 raise HTTPNotFound()
1779 raise HTTPNotFound()
1779
1780
1780 try:
1781 try:
1781 comment_history = CommentsModel().edit(
1782 comment_history = CommentsModel().edit(
1782 comment_id=comment_id,
1783 comment_id=comment_id,
1783 text=text,
1784 text=text,
1784 auth_user=self._rhodecode_user,
1785 auth_user=self._rhodecode_user,
1785 version=version,
1786 version=version,
1786 )
1787 )
1787 except CommentVersionMismatch:
1788 except CommentVersionMismatch:
1788 raise HTTPConflict()
1789 raise HTTPConflict()
1789
1790
1790 if not comment_history:
1791 if not comment_history:
1791 raise HTTPNotFound()
1792 raise HTTPNotFound()
1792
1793
1793 Session().commit()
1794 Session().commit()
1794
1795
1795 PullRequestModel().trigger_pull_request_hook(
1796 PullRequestModel().trigger_pull_request_hook(
1796 pull_request, self._rhodecode_user, 'comment_edit',
1797 pull_request, self._rhodecode_user, 'comment_edit',
1797 data={'comment': comment})
1798 data={'comment': comment})
1798
1799
1799 return {
1800 return {
1800 'comment_history_id': comment_history.comment_history_id,
1801 'comment_history_id': comment_history.comment_history_id,
1801 'comment_id': comment.comment_id,
1802 'comment_id': comment.comment_id,
1802 'comment_version': comment_history.version,
1803 'comment_version': comment_history.version,
1803 'comment_author_username': comment_history.author.username,
1804 'comment_author_username': comment_history.author.username,
1804 'comment_author_gravatar': h.gravatar_url(comment_history.author.email, 16),
1805 'comment_author_gravatar': h.gravatar_url(comment_history.author.email, 16),
1805 'comment_created_on': h.age_component(comment_history.created_on,
1806 'comment_created_on': h.age_component(comment_history.created_on,
1806 time_is_local=True),
1807 time_is_local=True),
1807 }
1808 }
1808 else:
1809 else:
1809 log.warning('No permissions for user %s to edit comment_id: %s',
1810 log.warning('No permissions for user %s to edit comment_id: %s',
1810 self._rhodecode_db_user, comment_id)
1811 self._rhodecode_db_user, comment_id)
1811 raise HTTPNotFound()
1812 raise HTTPNotFound()
@@ -1,3237 +1,3238 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 'variables';
8 @import 'variables';
9 @import 'bootstrap-variables';
9 @import 'bootstrap-variables';
10 @import 'form-bootstrap';
10 @import 'form-bootstrap';
11 @import 'codemirror';
11 @import 'codemirror';
12 @import 'legacy_code_styles';
12 @import 'legacy_code_styles';
13 @import 'readme-box';
13 @import 'readme-box';
14 @import 'progress-bar';
14 @import 'progress-bar';
15
15
16 @import 'type';
16 @import 'type';
17 @import 'alerts';
17 @import 'alerts';
18 @import 'buttons';
18 @import 'buttons';
19 @import 'tags';
19 @import 'tags';
20 @import 'code-block';
20 @import 'code-block';
21 @import 'examples';
21 @import 'examples';
22 @import 'login';
22 @import 'login';
23 @import 'main-content';
23 @import 'main-content';
24 @import 'select2';
24 @import 'select2';
25 @import 'comments';
25 @import 'comments';
26 @import 'panels-bootstrap';
26 @import 'panels-bootstrap';
27 @import 'panels';
27 @import 'panels';
28 @import 'deform';
28 @import 'deform';
29 @import 'tooltips';
29 @import 'tooltips';
30 @import 'sweetalert2';
30 @import 'sweetalert2';
31
31
32
32
33 //--- BASE ------------------//
33 //--- BASE ------------------//
34 .noscript-error {
34 .noscript-error {
35 top: 0;
35 top: 0;
36 left: 0;
36 left: 0;
37 width: 100%;
37 width: 100%;
38 z-index: 101;
38 z-index: 101;
39 text-align: center;
39 text-align: center;
40 font-size: 120%;
40 font-size: 120%;
41 color: white;
41 color: white;
42 background-color: @alert2;
42 background-color: @alert2;
43 padding: 5px 0 5px 0;
43 padding: 5px 0 5px 0;
44 font-weight: @text-semibold-weight;
44 font-weight: @text-semibold-weight;
45 font-family: @text-semibold;
45 font-family: @text-semibold;
46 }
46 }
47
47
48 html {
48 html {
49 display: table;
49 display: table;
50 height: 100%;
50 height: 100%;
51 width: 100%;
51 width: 100%;
52 }
52 }
53
53
54 body {
54 body {
55 display: table-cell;
55 display: table-cell;
56 width: 100%;
56 width: 100%;
57 }
57 }
58
58
59 //--- LAYOUT ------------------//
59 //--- LAYOUT ------------------//
60
60
61 .hidden{
61 .hidden{
62 display: none !important;
62 display: none !important;
63 }
63 }
64
64
65 .box{
65 .box{
66 float: left;
66 float: left;
67 width: 100%;
67 width: 100%;
68 }
68 }
69
69
70 .browser-header {
70 .browser-header {
71 clear: both;
71 clear: both;
72 }
72 }
73 .main {
73 .main {
74 clear: both;
74 clear: both;
75 padding:0 0 @pagepadding;
75 padding:0 0 @pagepadding;
76 height: auto;
76 height: auto;
77
77
78 &:after { //clearfix
78 &:after { //clearfix
79 content:"";
79 content:"";
80 clear:both;
80 clear:both;
81 width:100%;
81 width:100%;
82 display:block;
82 display:block;
83 }
83 }
84 }
84 }
85
85
86 .flex-container {
86 .flex-container {
87 display: flex;
87 display: flex;
88 justify-content: space-between;
88 justify-content: space-between;
89 }
89 }
90
90
91 .action-link{
91 .action-link{
92 margin-left: @padding;
92 margin-left: @padding;
93 padding-left: @padding;
93 padding-left: @padding;
94 border-left: @border-thickness solid @border-default-color;
94 border-left: @border-thickness solid @border-default-color;
95 }
95 }
96
96
97 .cursor-pointer {
97 .cursor-pointer {
98 cursor: pointer;
98 cursor: pointer;
99 }
99 }
100
100
101 input + .action-link, .action-link.first{
101 input + .action-link, .action-link.first{
102 border-left: none;
102 border-left: none;
103 }
103 }
104
104
105 .link-disabled {
105 .link-disabled {
106 color: @grey4;
106 color: @grey4;
107 cursor: default;
107 cursor: default;
108 }
108 }
109
109
110 .action-link.last{
110 .action-link.last{
111 margin-right: @padding;
111 margin-right: @padding;
112 padding-right: @padding;
112 padding-right: @padding;
113 }
113 }
114
114
115 .action-link.active,
115 .action-link.active,
116 .action-link.active a{
116 .action-link.active a{
117 color: @grey4;
117 color: @grey4;
118 }
118 }
119
119
120 .action-link.disabled {
120 .action-link.disabled {
121 color: @grey4;
121 color: @grey4;
122 cursor: inherit;
122 cursor: inherit;
123 }
123 }
124
124
125 .grey-link-action {
125 .grey-link-action {
126 cursor: pointer;
126 cursor: pointer;
127 &:hover {
127 &:hover {
128 color: @grey2;
128 color: @grey2;
129 }
129 }
130 color: @grey4;
130 color: @grey4;
131 }
131 }
132
132
133 .clipboard-action {
133 .clipboard-action {
134 cursor: pointer;
134 cursor: pointer;
135 margin-left: 5px;
135 margin-left: 5px;
136
136
137 &:not(.no-grey) {
137 &:not(.no-grey) {
138
138
139 &:hover {
139 &:hover {
140 color: @grey2;
140 color: @grey2;
141 }
141 }
142 color: @grey4;
142 color: @grey4;
143 }
143 }
144 }
144 }
145
145
146 ul.simple-list{
146 ul.simple-list{
147 list-style: none;
147 list-style: none;
148 margin: 0;
148 margin: 0;
149 padding: 0;
149 padding: 0;
150 }
150 }
151
151
152 .main-content {
152 .main-content {
153 padding-bottom: @pagepadding;
153 padding-bottom: @pagepadding;
154 }
154 }
155
155
156 .wide-mode-wrapper {
156 .wide-mode-wrapper {
157 max-width:4000px !important;
157 max-width:4000px !important;
158 }
158 }
159
159
160 .wrapper {
160 .wrapper {
161 position: relative;
161 position: relative;
162 max-width: @wrapper-maxwidth;
162 max-width: @wrapper-maxwidth;
163 margin: 0 auto;
163 margin: 0 auto;
164 }
164 }
165
165
166 #content {
166 #content {
167 clear: both;
167 clear: both;
168 padding: 0 @contentpadding;
168 padding: 0 @contentpadding;
169 }
169 }
170
170
171 .advanced-settings-fields{
171 .advanced-settings-fields{
172 input{
172 input{
173 margin-left: @textmargin;
173 margin-left: @textmargin;
174 margin-right: @padding/2;
174 margin-right: @padding/2;
175 }
175 }
176 }
176 }
177
177
178 .cs_files_title {
178 .cs_files_title {
179 margin: @pagepadding 0 0;
179 margin: @pagepadding 0 0;
180 }
180 }
181
181
182 input.inline[type="file"] {
182 input.inline[type="file"] {
183 display: inline;
183 display: inline;
184 }
184 }
185
185
186 .error_page {
186 .error_page {
187 margin: 10% auto;
187 margin: 10% auto;
188
188
189 h1 {
189 h1 {
190 color: @grey2;
190 color: @grey2;
191 }
191 }
192
192
193 .alert {
193 .alert {
194 margin: @padding 0;
194 margin: @padding 0;
195 }
195 }
196
196
197 .error-branding {
197 .error-branding {
198 color: @grey4;
198 color: @grey4;
199 font-weight: @text-semibold-weight;
199 font-weight: @text-semibold-weight;
200 font-family: @text-semibold;
200 font-family: @text-semibold;
201 }
201 }
202
202
203 .error_message {
203 .error_message {
204 font-family: @text-regular;
204 font-family: @text-regular;
205 }
205 }
206
206
207 .sidebar {
207 .sidebar {
208 min-height: 275px;
208 min-height: 275px;
209 margin: 0;
209 margin: 0;
210 padding: 0 0 @sidebarpadding @sidebarpadding;
210 padding: 0 0 @sidebarpadding @sidebarpadding;
211 border: none;
211 border: none;
212 }
212 }
213
213
214 .main-content {
214 .main-content {
215 position: relative;
215 position: relative;
216 margin: 0 @sidebarpadding @sidebarpadding;
216 margin: 0 @sidebarpadding @sidebarpadding;
217 padding: 0 0 0 @sidebarpadding;
217 padding: 0 0 0 @sidebarpadding;
218 border-left: @border-thickness solid @grey5;
218 border-left: @border-thickness solid @grey5;
219
219
220 @media (max-width:767px) {
220 @media (max-width:767px) {
221 clear: both;
221 clear: both;
222 width: 100%;
222 width: 100%;
223 margin: 0;
223 margin: 0;
224 border: none;
224 border: none;
225 }
225 }
226 }
226 }
227
227
228 .inner-column {
228 .inner-column {
229 float: left;
229 float: left;
230 width: 29.75%;
230 width: 29.75%;
231 min-height: 150px;
231 min-height: 150px;
232 margin: @sidebarpadding 2% 0 0;
232 margin: @sidebarpadding 2% 0 0;
233 padding: 0 2% 0 0;
233 padding: 0 2% 0 0;
234 border-right: @border-thickness solid @grey5;
234 border-right: @border-thickness solid @grey5;
235
235
236 @media (max-width:767px) {
236 @media (max-width:767px) {
237 clear: both;
237 clear: both;
238 width: 100%;
238 width: 100%;
239 border: none;
239 border: none;
240 }
240 }
241
241
242 ul {
242 ul {
243 padding-left: 1.25em;
243 padding-left: 1.25em;
244 }
244 }
245
245
246 &:last-child {
246 &:last-child {
247 margin: @sidebarpadding 0 0;
247 margin: @sidebarpadding 0 0;
248 border: none;
248 border: none;
249 }
249 }
250
250
251 h4 {
251 h4 {
252 margin: 0 0 @padding;
252 margin: 0 0 @padding;
253 font-weight: @text-semibold-weight;
253 font-weight: @text-semibold-weight;
254 font-family: @text-semibold;
254 font-family: @text-semibold;
255 }
255 }
256 }
256 }
257 }
257 }
258 .error-page-logo {
258 .error-page-logo {
259 width: 130px;
259 width: 130px;
260 height: 160px;
260 height: 160px;
261 }
261 }
262
262
263 // HEADER
263 // HEADER
264 .header {
264 .header {
265
265
266 // TODO: johbo: Fix login pages, so that they work without a min-height
266 // TODO: johbo: Fix login pages, so that they work without a min-height
267 // for the header and then remove the min-height. I chose a smaller value
267 // for the header and then remove the min-height. I chose a smaller value
268 // intentionally here to avoid rendering issues in the main navigation.
268 // intentionally here to avoid rendering issues in the main navigation.
269 min-height: 49px;
269 min-height: 49px;
270 min-width: 1024px;
270 min-width: 1024px;
271
271
272 position: relative;
272 position: relative;
273 vertical-align: bottom;
273 vertical-align: bottom;
274 padding: 0 @header-padding;
274 padding: 0 @header-padding;
275 background-color: @grey1;
275 background-color: @grey1;
276 color: @grey5;
276 color: @grey5;
277
277
278 .title {
278 .title {
279 overflow: visible;
279 overflow: visible;
280 }
280 }
281
281
282 &:before,
282 &:before,
283 &:after {
283 &:after {
284 content: "";
284 content: "";
285 clear: both;
285 clear: both;
286 width: 100%;
286 width: 100%;
287 }
287 }
288
288
289 // TODO: johbo: Avoids breaking "Repositories" chooser
289 // TODO: johbo: Avoids breaking "Repositories" chooser
290 .select2-container .select2-choice .select2-arrow {
290 .select2-container .select2-choice .select2-arrow {
291 display: none;
291 display: none;
292 }
292 }
293 }
293 }
294
294
295 #header-inner {
295 #header-inner {
296 &.title {
296 &.title {
297 margin: 0;
297 margin: 0;
298 }
298 }
299 &:before,
299 &:before,
300 &:after {
300 &:after {
301 content: "";
301 content: "";
302 clear: both;
302 clear: both;
303 }
303 }
304 }
304 }
305
305
306 // Gists
306 // Gists
307 #files_data {
307 #files_data {
308 clear: both; //for firefox
308 clear: both; //for firefox
309 padding-top: 10px;
309 padding-top: 10px;
310 }
310 }
311
311
312 #gistid {
312 #gistid {
313 margin-right: @padding;
313 margin-right: @padding;
314 }
314 }
315
315
316 // Global Settings Editor
316 // Global Settings Editor
317 .textarea.editor {
317 .textarea.editor {
318 float: left;
318 float: left;
319 position: relative;
319 position: relative;
320 max-width: @texteditor-width;
320 max-width: @texteditor-width;
321
321
322 select {
322 select {
323 position: absolute;
323 position: absolute;
324 top:10px;
324 top:10px;
325 right:0;
325 right:0;
326 }
326 }
327
327
328 .CodeMirror {
328 .CodeMirror {
329 margin: 0;
329 margin: 0;
330 }
330 }
331
331
332 .help-block {
332 .help-block {
333 margin: 0 0 @padding;
333 margin: 0 0 @padding;
334 padding:.5em;
334 padding:.5em;
335 background-color: @grey6;
335 background-color: @grey6;
336 &.pre-formatting {
336 &.pre-formatting {
337 white-space: pre;
337 white-space: pre;
338 }
338 }
339 }
339 }
340 }
340 }
341
341
342 ul.auth_plugins {
342 ul.auth_plugins {
343 margin: @padding 0 @padding @legend-width;
343 margin: @padding 0 @padding @legend-width;
344 padding: 0;
344 padding: 0;
345
345
346 li {
346 li {
347 margin-bottom: @padding;
347 margin-bottom: @padding;
348 line-height: 1em;
348 line-height: 1em;
349 list-style-type: none;
349 list-style-type: none;
350
350
351 .auth_buttons .btn {
351 .auth_buttons .btn {
352 margin-right: @padding;
352 margin-right: @padding;
353 }
353 }
354
354
355 }
355 }
356 }
356 }
357
357
358
358
359 // My Account PR list
359 // My Account PR list
360
360
361 #show_closed {
361 #show_closed {
362 margin: 0 1em 0 0;
362 margin: 0 1em 0 0;
363 }
363 }
364
364
365 #pull_request_list_table {
365 #pull_request_list_table {
366 .closed {
366 .closed {
367 background-color: @grey6;
367 background-color: @grey6;
368 }
368 }
369
369
370 .state-creating,
370 .state-creating,
371 .state-updating,
371 .state-updating,
372 .state-merging
372 .state-merging
373 {
373 {
374 background-color: @grey6;
374 background-color: @grey6;
375 }
375 }
376
376
377 .td-status {
378 padding-left: .5em;
379 }
380 .log-container .truncate {
377 .log-container .truncate {
381 height: 2.75em;
378 height: 2.75em;
382 white-space: pre-line;
379 white-space: pre-line;
383 }
380 }
384 table.rctable .user {
381 table.rctable .user {
385 padding-left: 0;
382 padding-left: 0;
386 }
383 }
384 .td-status {
385 padding: 0 0px 0px 10px;
386 width: 15px;
387 }
387 table.rctable {
388 table.rctable {
388 td.td-description,
389 td.td-description,
389 .rc-user {
390 .rc-user {
390 min-width: auto;
391 min-width: auto;
391 }
392 }
392 }
393 }
393 }
394 }
394
395
395 // Pull Requests
396 // Pull Requests
396
397
397 .pullrequests_section_head {
398 .pullrequests_section_head {
398 display: block;
399 display: block;
399 clear: both;
400 clear: both;
400 margin: @padding 0;
401 margin: @padding 0;
401 font-weight: @text-bold-weight;
402 font-weight: @text-bold-weight;
402 font-family: @text-bold;
403 font-family: @text-bold;
403 }
404 }
404
405
405 .pr-commit-flow {
406 .pr-commit-flow {
406 position: relative;
407 position: relative;
407 font-weight: 600;
408 font-weight: 600;
408
409
409 .tag {
410 .tag {
410 display: inline-block;
411 display: inline-block;
411 margin: 0 1em .5em 0;
412 margin: 0 1em .5em 0;
412 }
413 }
413
414
414 .clone-url {
415 .clone-url {
415 display: inline-block;
416 display: inline-block;
416 margin: 0 0 .5em 0;
417 margin: 0 0 .5em 0;
417 padding: 0;
418 padding: 0;
418 line-height: 1.2em;
419 line-height: 1.2em;
419 }
420 }
420 }
421 }
421
422
422 .pr-mergeinfo {
423 .pr-mergeinfo {
423 min-width: 95% !important;
424 min-width: 95% !important;
424 padding: 0 !important;
425 padding: 0 !important;
425 border: 0;
426 border: 0;
426 }
427 }
427 .pr-mergeinfo-copy {
428 .pr-mergeinfo-copy {
428 padding: 0 0;
429 padding: 0 0;
429 }
430 }
430
431
431 .pr-pullinfo {
432 .pr-pullinfo {
432 min-width: 95% !important;
433 min-width: 95% !important;
433 padding: 0 !important;
434 padding: 0 !important;
434 border: 0;
435 border: 0;
435 }
436 }
436 .pr-pullinfo-copy {
437 .pr-pullinfo-copy {
437 padding: 0 0;
438 padding: 0 0;
438 }
439 }
439
440
440 .pr-title-input {
441 .pr-title-input {
441 width: 100%;
442 width: 100%;
442 font-size: 18px;
443 font-size: 18px;
443 margin: 0 0 4px 0;
444 margin: 0 0 4px 0;
444 padding: 0;
445 padding: 0;
445 line-height: 1.7em;
446 line-height: 1.7em;
446 color: @text-color;
447 color: @text-color;
447 letter-spacing: .02em;
448 letter-spacing: .02em;
448 font-weight: @text-bold-weight;
449 font-weight: @text-bold-weight;
449 font-family: @text-bold;
450 font-family: @text-bold;
450
451
451 &:hover {
452 &:hover {
452 box-shadow: none;
453 box-shadow: none;
453 }
454 }
454 }
455 }
455
456
456 #pr-title {
457 #pr-title {
457 input {
458 input {
458 border: 1px transparent;
459 border: 1px transparent;
459 color: black;
460 color: black;
460 opacity: 1;
461 opacity: 1;
461 background: #fff;
462 background: #fff;
462 font-size: 18px;
463 font-size: 18px;
463 }
464 }
464 }
465 }
465
466
466 .pr-title-closed-tag {
467 .pr-title-closed-tag {
467 font-size: 16px;
468 font-size: 16px;
468 }
469 }
469
470
470 #pr-desc {
471 #pr-desc {
471 padding: 10px 0;
472 padding: 10px 0;
472
473
473 .markdown-block {
474 .markdown-block {
474 padding: 0;
475 padding: 0;
475 margin-bottom: -30px;
476 margin-bottom: -30px;
476 }
477 }
477 }
478 }
478
479
479 #pullrequest_title {
480 #pullrequest_title {
480 width: 100%;
481 width: 100%;
481 box-sizing: border-box;
482 box-sizing: border-box;
482 }
483 }
483
484
484 #pr_open_message {
485 #pr_open_message {
485 border: @border-thickness solid #fff;
486 border: @border-thickness solid #fff;
486 border-radius: @border-radius;
487 border-radius: @border-radius;
487 text-align: left;
488 text-align: left;
488 overflow: hidden;
489 overflow: hidden;
489 white-space: pre-line;
490 white-space: pre-line;
490 padding-top: 5px
491 padding-top: 5px
491 }
492 }
492
493
493 #add_reviewer {
494 #add_reviewer {
494 padding-top: 10px;
495 padding-top: 10px;
495 }
496 }
496
497
497 #add_reviewer_input {
498 #add_reviewer_input {
498 padding-top: 10px
499 padding-top: 10px
499 }
500 }
500
501
501 .pr-details-title-author-pref {
502 .pr-details-title-author-pref {
502 padding-right: 10px
503 padding-right: 10px
503 }
504 }
504
505
505 .label-pr-detail {
506 .label-pr-detail {
506 display: table-cell;
507 display: table-cell;
507 width: 120px;
508 width: 120px;
508 padding-top: 7.5px;
509 padding-top: 7.5px;
509 padding-bottom: 7.5px;
510 padding-bottom: 7.5px;
510 padding-right: 7.5px;
511 padding-right: 7.5px;
511 }
512 }
512
513
513 .source-details ul {
514 .source-details ul {
514 padding: 10px 16px;
515 padding: 10px 16px;
515 }
516 }
516
517
517 .source-details-action {
518 .source-details-action {
518 color: @grey4;
519 color: @grey4;
519 font-size: 11px
520 font-size: 11px
520 }
521 }
521
522
522 .pr-submit-button {
523 .pr-submit-button {
523 float: right;
524 float: right;
524 margin: 0 0 0 5px;
525 margin: 0 0 0 5px;
525 }
526 }
526
527
527 .pr-spacing-container {
528 .pr-spacing-container {
528 padding: 20px;
529 padding: 20px;
529 clear: both
530 clear: both
530 }
531 }
531
532
532 #pr-description-input {
533 #pr-description-input {
533 margin-bottom: 0;
534 margin-bottom: 0;
534 }
535 }
535
536
536 .pr-description-label {
537 .pr-description-label {
537 vertical-align: top;
538 vertical-align: top;
538 }
539 }
539
540
540 #open_edit_pullrequest {
541 #open_edit_pullrequest {
541 padding: 0;
542 padding: 0;
542 }
543 }
543
544
544 #close_edit_pullrequest {
545 #close_edit_pullrequest {
545
546
546 }
547 }
547
548
548 #delete_pullrequest {
549 #delete_pullrequest {
549 clear: inherit;
550 clear: inherit;
550
551
551 form {
552 form {
552 display: inline;
553 display: inline;
553 }
554 }
554
555
555 }
556 }
556
557
557 .perms_section_head {
558 .perms_section_head {
558 min-width: 625px;
559 min-width: 625px;
559
560
560 h2 {
561 h2 {
561 margin-bottom: 0;
562 margin-bottom: 0;
562 }
563 }
563
564
564 .label-checkbox {
565 .label-checkbox {
565 float: left;
566 float: left;
566 }
567 }
567
568
568 &.field {
569 &.field {
569 margin: @space 0 @padding;
570 margin: @space 0 @padding;
570 }
571 }
571
572
572 &:first-child.field {
573 &:first-child.field {
573 margin-top: 0;
574 margin-top: 0;
574
575
575 .label {
576 .label {
576 margin-top: 0;
577 margin-top: 0;
577 padding-top: 0;
578 padding-top: 0;
578 }
579 }
579
580
580 .radios {
581 .radios {
581 padding-top: 0;
582 padding-top: 0;
582 }
583 }
583 }
584 }
584
585
585 .radios {
586 .radios {
586 position: relative;
587 position: relative;
587 width: 505px;
588 width: 505px;
588 }
589 }
589 }
590 }
590
591
591 //--- MODULES ------------------//
592 //--- MODULES ------------------//
592
593
593
594
594 // Server Announcement
595 // Server Announcement
595 #server-announcement {
596 #server-announcement {
596 width: 95%;
597 width: 95%;
597 margin: @padding auto;
598 margin: @padding auto;
598 padding: @padding;
599 padding: @padding;
599 border-width: 2px;
600 border-width: 2px;
600 border-style: solid;
601 border-style: solid;
601 .border-radius(2px);
602 .border-radius(2px);
602 font-weight: @text-bold-weight;
603 font-weight: @text-bold-weight;
603 font-family: @text-bold;
604 font-family: @text-bold;
604
605
605 &.info { border-color: @alert4; background-color: @alert4-inner; }
606 &.info { border-color: @alert4; background-color: @alert4-inner; }
606 &.warning { border-color: @alert3; background-color: @alert3-inner; }
607 &.warning { border-color: @alert3; background-color: @alert3-inner; }
607 &.error { border-color: @alert2; background-color: @alert2-inner; }
608 &.error { border-color: @alert2; background-color: @alert2-inner; }
608 &.success { border-color: @alert1; background-color: @alert1-inner; }
609 &.success { border-color: @alert1; background-color: @alert1-inner; }
609 &.neutral { border-color: @grey3; background-color: @grey6; }
610 &.neutral { border-color: @grey3; background-color: @grey6; }
610 }
611 }
611
612
612 // Fixed Sidebar Column
613 // Fixed Sidebar Column
613 .sidebar-col-wrapper {
614 .sidebar-col-wrapper {
614 padding-left: @sidebar-all-width;
615 padding-left: @sidebar-all-width;
615
616
616 .sidebar {
617 .sidebar {
617 width: @sidebar-width;
618 width: @sidebar-width;
618 margin-left: -@sidebar-all-width;
619 margin-left: -@sidebar-all-width;
619 }
620 }
620 }
621 }
621
622
622 .sidebar-col-wrapper.scw-small {
623 .sidebar-col-wrapper.scw-small {
623 padding-left: @sidebar-small-all-width;
624 padding-left: @sidebar-small-all-width;
624
625
625 .sidebar {
626 .sidebar {
626 width: @sidebar-small-width;
627 width: @sidebar-small-width;
627 margin-left: -@sidebar-small-all-width;
628 margin-left: -@sidebar-small-all-width;
628 }
629 }
629 }
630 }
630
631
631
632
632 // FOOTER
633 // FOOTER
633 #footer {
634 #footer {
634 padding: 0;
635 padding: 0;
635 text-align: center;
636 text-align: center;
636 vertical-align: middle;
637 vertical-align: middle;
637 color: @grey2;
638 color: @grey2;
638 font-size: 11px;
639 font-size: 11px;
639
640
640 p {
641 p {
641 margin: 0;
642 margin: 0;
642 padding: 1em;
643 padding: 1em;
643 line-height: 1em;
644 line-height: 1em;
644 }
645 }
645
646
646 .server-instance { //server instance
647 .server-instance { //server instance
647 display: none;
648 display: none;
648 }
649 }
649
650
650 .title {
651 .title {
651 float: none;
652 float: none;
652 margin: 0 auto;
653 margin: 0 auto;
653 }
654 }
654 }
655 }
655
656
656 button.close {
657 button.close {
657 padding: 0;
658 padding: 0;
658 cursor: pointer;
659 cursor: pointer;
659 background: transparent;
660 background: transparent;
660 border: 0;
661 border: 0;
661 .box-shadow(none);
662 .box-shadow(none);
662 -webkit-appearance: none;
663 -webkit-appearance: none;
663 }
664 }
664
665
665 .close {
666 .close {
666 float: right;
667 float: right;
667 font-size: 21px;
668 font-size: 21px;
668 font-family: @text-bootstrap;
669 font-family: @text-bootstrap;
669 line-height: 1em;
670 line-height: 1em;
670 font-weight: bold;
671 font-weight: bold;
671 color: @grey2;
672 color: @grey2;
672
673
673 &:hover,
674 &:hover,
674 &:focus {
675 &:focus {
675 color: @grey1;
676 color: @grey1;
676 text-decoration: none;
677 text-decoration: none;
677 cursor: pointer;
678 cursor: pointer;
678 }
679 }
679 }
680 }
680
681
681 // GRID
682 // GRID
682 .sorting,
683 .sorting,
683 .sorting_desc,
684 .sorting_desc,
684 .sorting_asc {
685 .sorting_asc {
685 cursor: pointer;
686 cursor: pointer;
686 }
687 }
687 .sorting_desc:after {
688 .sorting_desc:after {
688 content: "\00A0\25B2";
689 content: "\00A0\25B2";
689 font-size: .75em;
690 font-size: .75em;
690 }
691 }
691 .sorting_asc:after {
692 .sorting_asc:after {
692 content: "\00A0\25BC";
693 content: "\00A0\25BC";
693 font-size: .68em;
694 font-size: .68em;
694 }
695 }
695
696
696
697
697 .user_auth_tokens {
698 .user_auth_tokens {
698
699
699 &.truncate {
700 &.truncate {
700 white-space: nowrap;
701 white-space: nowrap;
701 overflow: hidden;
702 overflow: hidden;
702 text-overflow: ellipsis;
703 text-overflow: ellipsis;
703 }
704 }
704
705
705 .fields .field .input {
706 .fields .field .input {
706 margin: 0;
707 margin: 0;
707 }
708 }
708
709
709 input#description {
710 input#description {
710 width: 100px;
711 width: 100px;
711 margin: 0;
712 margin: 0;
712 }
713 }
713
714
714 .drop-menu {
715 .drop-menu {
715 // TODO: johbo: Remove this, should work out of the box when
716 // TODO: johbo: Remove this, should work out of the box when
716 // having multiple inputs inline
717 // having multiple inputs inline
717 margin: 0 0 0 5px;
718 margin: 0 0 0 5px;
718 }
719 }
719 }
720 }
720 #user_list_table {
721 #user_list_table {
721 .closed {
722 .closed {
722 background-color: @grey6;
723 background-color: @grey6;
723 }
724 }
724 }
725 }
725
726
726
727
727 input, textarea {
728 input, textarea {
728 &.disabled {
729 &.disabled {
729 opacity: .5;
730 opacity: .5;
730 }
731 }
731
732
732 &:hover {
733 &:hover {
733 border-color: @grey3;
734 border-color: @grey3;
734 box-shadow: @button-shadow;
735 box-shadow: @button-shadow;
735 }
736 }
736
737
737 &:focus {
738 &:focus {
738 border-color: @rcblue;
739 border-color: @rcblue;
739 box-shadow: @button-shadow;
740 box-shadow: @button-shadow;
740 }
741 }
741 }
742 }
742
743
743 // remove extra padding in firefox
744 // remove extra padding in firefox
744 input::-moz-focus-inner { border:0; padding:0 }
745 input::-moz-focus-inner { border:0; padding:0 }
745
746
746 .adjacent input {
747 .adjacent input {
747 margin-bottom: @padding;
748 margin-bottom: @padding;
748 }
749 }
749
750
750 .permissions_boxes {
751 .permissions_boxes {
751 display: block;
752 display: block;
752 }
753 }
753
754
754 //FORMS
755 //FORMS
755
756
756 .medium-inline,
757 .medium-inline,
757 input#description.medium-inline {
758 input#description.medium-inline {
758 display: inline;
759 display: inline;
759 width: @medium-inline-input-width;
760 width: @medium-inline-input-width;
760 min-width: 100px;
761 min-width: 100px;
761 }
762 }
762
763
763 select {
764 select {
764 //reset
765 //reset
765 -webkit-appearance: none;
766 -webkit-appearance: none;
766 -moz-appearance: none;
767 -moz-appearance: none;
767
768
768 display: inline-block;
769 display: inline-block;
769 height: 28px;
770 height: 28px;
770 width: auto;
771 width: auto;
771 margin: 0 @padding @padding 0;
772 margin: 0 @padding @padding 0;
772 padding: 0 18px 0 8px;
773 padding: 0 18px 0 8px;
773 line-height:1em;
774 line-height:1em;
774 font-size: @basefontsize;
775 font-size: @basefontsize;
775 border: @border-thickness solid @grey5;
776 border: @border-thickness solid @grey5;
776 border-radius: @border-radius;
777 border-radius: @border-radius;
777 background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%;
778 background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%;
778 color: @grey4;
779 color: @grey4;
779 box-shadow: @button-shadow;
780 box-shadow: @button-shadow;
780
781
781 &:after {
782 &:after {
782 content: "\00A0\25BE";
783 content: "\00A0\25BE";
783 }
784 }
784
785
785 &:focus, &:hover {
786 &:focus, &:hover {
786 outline: none;
787 outline: none;
787 border-color: @grey4;
788 border-color: @grey4;
788 color: @rcdarkblue;
789 color: @rcdarkblue;
789 }
790 }
790 }
791 }
791
792
792 option {
793 option {
793 &:focus {
794 &:focus {
794 outline: none;
795 outline: none;
795 }
796 }
796 }
797 }
797
798
798 input,
799 input,
799 textarea {
800 textarea {
800 padding: @input-padding;
801 padding: @input-padding;
801 border: @input-border-thickness solid @border-highlight-color;
802 border: @input-border-thickness solid @border-highlight-color;
802 .border-radius (@border-radius);
803 .border-radius (@border-radius);
803 font-family: @text-light;
804 font-family: @text-light;
804 font-size: @basefontsize;
805 font-size: @basefontsize;
805
806
806 &.input-sm {
807 &.input-sm {
807 padding: 5px;
808 padding: 5px;
808 }
809 }
809
810
810 &#description {
811 &#description {
811 min-width: @input-description-minwidth;
812 min-width: @input-description-minwidth;
812 min-height: 1em;
813 min-height: 1em;
813 padding: 10px;
814 padding: 10px;
814 }
815 }
815 }
816 }
816
817
817 .field-sm {
818 .field-sm {
818 input,
819 input,
819 textarea {
820 textarea {
820 padding: 5px;
821 padding: 5px;
821 }
822 }
822 }
823 }
823
824
824 textarea {
825 textarea {
825 display: block;
826 display: block;
826 clear: both;
827 clear: both;
827 width: 100%;
828 width: 100%;
828 min-height: 100px;
829 min-height: 100px;
829 margin-bottom: @padding;
830 margin-bottom: @padding;
830 .box-sizing(border-box);
831 .box-sizing(border-box);
831 overflow: auto;
832 overflow: auto;
832 }
833 }
833
834
834 label {
835 label {
835 font-family: @text-light;
836 font-family: @text-light;
836 }
837 }
837
838
838 // GRAVATARS
839 // GRAVATARS
839 // centers gravatar on username to the right
840 // centers gravatar on username to the right
840
841
841 .gravatar {
842 .gravatar {
842 display: inline;
843 display: inline;
843 min-width: 16px;
844 min-width: 16px;
844 min-height: 16px;
845 min-height: 16px;
845 margin: -5px 0;
846 margin: -5px 0;
846 padding: 0;
847 padding: 0;
847 line-height: 1em;
848 line-height: 1em;
848 box-sizing: content-box;
849 box-sizing: content-box;
849 border-radius: 50%;
850 border-radius: 50%;
850
851
851 &.gravatar-large {
852 &.gravatar-large {
852 margin: -0.5em .25em -0.5em 0;
853 margin: -0.5em .25em -0.5em 0;
853 }
854 }
854
855
855 & + .user {
856 & + .user {
856 display: inline;
857 display: inline;
857 margin: 0;
858 margin: 0;
858 padding: 0 0 0 .17em;
859 padding: 0 0 0 .17em;
859 line-height: 1em;
860 line-height: 1em;
860 }
861 }
861
862
862 & + .no-margin {
863 & + .no-margin {
863 margin: 0
864 margin: 0
864 }
865 }
865
866
866 }
867 }
867
868
868 .user-inline-data {
869 .user-inline-data {
869 display: inline-block;
870 display: inline-block;
870 float: left;
871 float: left;
871 padding-left: .5em;
872 padding-left: .5em;
872 line-height: 1.3em;
873 line-height: 1.3em;
873 }
874 }
874
875
875 .rc-user { // gravatar + user wrapper
876 .rc-user { // gravatar + user wrapper
876 float: left;
877 float: left;
877 position: relative;
878 position: relative;
878 min-width: 100px;
879 min-width: 100px;
879 max-width: 200px;
880 max-width: 200px;
880 min-height: (@gravatar-size + @border-thickness * 2); // account for border
881 min-height: (@gravatar-size + @border-thickness * 2); // account for border
881 display: block;
882 display: block;
882 padding: 0 0 0 (@gravatar-size + @basefontsize/4);
883 padding: 0 0 0 (@gravatar-size + @basefontsize/4);
883
884
884
885
885 .gravatar {
886 .gravatar {
886 display: block;
887 display: block;
887 position: absolute;
888 position: absolute;
888 top: 0;
889 top: 0;
889 left: 0;
890 left: 0;
890 min-width: @gravatar-size;
891 min-width: @gravatar-size;
891 min-height: @gravatar-size;
892 min-height: @gravatar-size;
892 margin: 0;
893 margin: 0;
893 }
894 }
894
895
895 .user {
896 .user {
896 display: block;
897 display: block;
897 max-width: 175px;
898 max-width: 175px;
898 padding-top: 2px;
899 padding-top: 2px;
899 overflow: hidden;
900 overflow: hidden;
900 text-overflow: ellipsis;
901 text-overflow: ellipsis;
901 }
902 }
902 }
903 }
903
904
904 .gist-gravatar,
905 .gist-gravatar,
905 .journal_container {
906 .journal_container {
906 .gravatar-large {
907 .gravatar-large {
907 margin: 0 .5em -10px 0;
908 margin: 0 .5em -10px 0;
908 }
909 }
909 }
910 }
910
911
911 .gist-type-fields {
912 .gist-type-fields {
912 line-height: 30px;
913 line-height: 30px;
913 height: 30px;
914 height: 30px;
914
915
915 .gist-type-fields-wrapper {
916 .gist-type-fields-wrapper {
916 vertical-align: middle;
917 vertical-align: middle;
917 display: inline-block;
918 display: inline-block;
918 line-height: 25px;
919 line-height: 25px;
919 }
920 }
920 }
921 }
921
922
922 // ADMIN SETTINGS
923 // ADMIN SETTINGS
923
924
924 // Tag Patterns
925 // Tag Patterns
925 .tag_patterns {
926 .tag_patterns {
926 .tag_input {
927 .tag_input {
927 margin-bottom: @padding;
928 margin-bottom: @padding;
928 }
929 }
929 }
930 }
930
931
931 .locked_input {
932 .locked_input {
932 position: relative;
933 position: relative;
933
934
934 input {
935 input {
935 display: inline;
936 display: inline;
936 margin: 3px 5px 0px 0px;
937 margin: 3px 5px 0px 0px;
937 }
938 }
938
939
939 br {
940 br {
940 display: none;
941 display: none;
941 }
942 }
942
943
943 .error-message {
944 .error-message {
944 float: left;
945 float: left;
945 width: 100%;
946 width: 100%;
946 }
947 }
947
948
948 .lock_input_button {
949 .lock_input_button {
949 display: inline;
950 display: inline;
950 }
951 }
951
952
952 .help-block {
953 .help-block {
953 clear: both;
954 clear: both;
954 }
955 }
955 }
956 }
956
957
957 // Notifications
958 // Notifications
958
959
959 .notifications_buttons {
960 .notifications_buttons {
960 margin: 0 0 @space 0;
961 margin: 0 0 @space 0;
961 padding: 0;
962 padding: 0;
962
963
963 .btn {
964 .btn {
964 display: inline-block;
965 display: inline-block;
965 }
966 }
966 }
967 }
967
968
968 .notification-list {
969 .notification-list {
969
970
970 div {
971 div {
971 vertical-align: middle;
972 vertical-align: middle;
972 }
973 }
973
974
974 .container {
975 .container {
975 display: block;
976 display: block;
976 margin: 0 0 @padding 0;
977 margin: 0 0 @padding 0;
977 }
978 }
978
979
979 .delete-notifications {
980 .delete-notifications {
980 margin-left: @padding;
981 margin-left: @padding;
981 text-align: right;
982 text-align: right;
982 cursor: pointer;
983 cursor: pointer;
983 }
984 }
984
985
985 .read-notifications {
986 .read-notifications {
986 margin-left: @padding/2;
987 margin-left: @padding/2;
987 text-align: right;
988 text-align: right;
988 width: 35px;
989 width: 35px;
989 cursor: pointer;
990 cursor: pointer;
990 }
991 }
991
992
992 .icon-minus-sign {
993 .icon-minus-sign {
993 color: @alert2;
994 color: @alert2;
994 }
995 }
995
996
996 .icon-ok-sign {
997 .icon-ok-sign {
997 color: @alert1;
998 color: @alert1;
998 }
999 }
999 }
1000 }
1000
1001
1001 .user_settings {
1002 .user_settings {
1002 float: left;
1003 float: left;
1003 clear: both;
1004 clear: both;
1004 display: block;
1005 display: block;
1005 width: 100%;
1006 width: 100%;
1006
1007
1007 .gravatar_box {
1008 .gravatar_box {
1008 margin-bottom: @padding;
1009 margin-bottom: @padding;
1009
1010
1010 &:after {
1011 &:after {
1011 content: " ";
1012 content: " ";
1012 clear: both;
1013 clear: both;
1013 width: 100%;
1014 width: 100%;
1014 }
1015 }
1015 }
1016 }
1016
1017
1017 .fields .field {
1018 .fields .field {
1018 clear: both;
1019 clear: both;
1019 }
1020 }
1020 }
1021 }
1021
1022
1022 .advanced_settings {
1023 .advanced_settings {
1023 margin-bottom: @space;
1024 margin-bottom: @space;
1024
1025
1025 .help-block {
1026 .help-block {
1026 margin-left: 0;
1027 margin-left: 0;
1027 }
1028 }
1028
1029
1029 button + .help-block {
1030 button + .help-block {
1030 margin-top: @padding;
1031 margin-top: @padding;
1031 }
1032 }
1032 }
1033 }
1033
1034
1034 // admin settings radio buttons and labels
1035 // admin settings radio buttons and labels
1035 .label-2 {
1036 .label-2 {
1036 float: left;
1037 float: left;
1037 width: @label2-width;
1038 width: @label2-width;
1038
1039
1039 label {
1040 label {
1040 color: @grey1;
1041 color: @grey1;
1041 }
1042 }
1042 }
1043 }
1043 .checkboxes {
1044 .checkboxes {
1044 float: left;
1045 float: left;
1045 width: @checkboxes-width;
1046 width: @checkboxes-width;
1046 margin-bottom: @padding;
1047 margin-bottom: @padding;
1047
1048
1048 .checkbox {
1049 .checkbox {
1049 width: 100%;
1050 width: 100%;
1050
1051
1051 label {
1052 label {
1052 margin: 0;
1053 margin: 0;
1053 padding: 0;
1054 padding: 0;
1054 }
1055 }
1055 }
1056 }
1056
1057
1057 .checkbox + .checkbox {
1058 .checkbox + .checkbox {
1058 display: inline-block;
1059 display: inline-block;
1059 }
1060 }
1060
1061
1061 label {
1062 label {
1062 margin-right: 1em;
1063 margin-right: 1em;
1063 }
1064 }
1064 }
1065 }
1065
1066
1066 // CHANGELOG
1067 // CHANGELOG
1067 .container_header {
1068 .container_header {
1068 float: left;
1069 float: left;
1069 display: block;
1070 display: block;
1070 width: 100%;
1071 width: 100%;
1071 margin: @padding 0 @padding;
1072 margin: @padding 0 @padding;
1072
1073
1073 #filter_changelog {
1074 #filter_changelog {
1074 float: left;
1075 float: left;
1075 margin-right: @padding;
1076 margin-right: @padding;
1076 }
1077 }
1077
1078
1078 .breadcrumbs_light {
1079 .breadcrumbs_light {
1079 display: inline-block;
1080 display: inline-block;
1080 }
1081 }
1081 }
1082 }
1082
1083
1083 .info_box {
1084 .info_box {
1084 float: right;
1085 float: right;
1085 }
1086 }
1086
1087
1087
1088
1088
1089
1089 #graph_content{
1090 #graph_content{
1090
1091
1091 // adjust for table headers so that graph renders properly
1092 // adjust for table headers so that graph renders properly
1092 // #graph_nodes padding - table cell padding
1093 // #graph_nodes padding - table cell padding
1093 padding-top: (@space - (@basefontsize * 2.4));
1094 padding-top: (@space - (@basefontsize * 2.4));
1094
1095
1095 &.graph_full_width {
1096 &.graph_full_width {
1096 width: 100%;
1097 width: 100%;
1097 max-width: 100%;
1098 max-width: 100%;
1098 }
1099 }
1099 }
1100 }
1100
1101
1101 #graph {
1102 #graph {
1102
1103
1103 .pagination-left {
1104 .pagination-left {
1104 float: left;
1105 float: left;
1105 clear: both;
1106 clear: both;
1106 }
1107 }
1107
1108
1108 .log-container {
1109 .log-container {
1109 max-width: 345px;
1110 max-width: 345px;
1110
1111
1111 .message{
1112 .message{
1112 max-width: 340px;
1113 max-width: 340px;
1113 }
1114 }
1114 }
1115 }
1115
1116
1116 .graph-col-wrapper {
1117 .graph-col-wrapper {
1117
1118
1118 #graph_nodes {
1119 #graph_nodes {
1119 width: 100px;
1120 width: 100px;
1120 position: absolute;
1121 position: absolute;
1121 left: 70px;
1122 left: 70px;
1122 z-index: -1;
1123 z-index: -1;
1123 }
1124 }
1124 }
1125 }
1125
1126
1126 .load-more-commits {
1127 .load-more-commits {
1127 text-align: center;
1128 text-align: center;
1128 }
1129 }
1129 .load-more-commits:hover {
1130 .load-more-commits:hover {
1130 background-color: @grey7;
1131 background-color: @grey7;
1131 }
1132 }
1132 .load-more-commits {
1133 .load-more-commits {
1133 a {
1134 a {
1134 display: block;
1135 display: block;
1135 }
1136 }
1136 }
1137 }
1137 }
1138 }
1138
1139
1139 .obsolete-toggle {
1140 .obsolete-toggle {
1140 line-height: 30px;
1141 line-height: 30px;
1141 margin-left: -15px;
1142 margin-left: -15px;
1142 }
1143 }
1143
1144
1144 #rev_range_container, #rev_range_clear, #rev_range_more {
1145 #rev_range_container, #rev_range_clear, #rev_range_more {
1145 margin-top: -5px;
1146 margin-top: -5px;
1146 margin-bottom: -5px;
1147 margin-bottom: -5px;
1147 }
1148 }
1148
1149
1149 #filter_changelog {
1150 #filter_changelog {
1150 float: left;
1151 float: left;
1151 }
1152 }
1152
1153
1153
1154
1154 //--- THEME ------------------//
1155 //--- THEME ------------------//
1155
1156
1156 #logo {
1157 #logo {
1157 float: left;
1158 float: left;
1158 margin: 9px 0 0 0;
1159 margin: 9px 0 0 0;
1159
1160
1160 .header {
1161 .header {
1161 background-color: transparent;
1162 background-color: transparent;
1162 }
1163 }
1163
1164
1164 a {
1165 a {
1165 display: inline-block;
1166 display: inline-block;
1166 }
1167 }
1167
1168
1168 img {
1169 img {
1169 height:30px;
1170 height:30px;
1170 }
1171 }
1171 }
1172 }
1172
1173
1173 .logo-wrapper {
1174 .logo-wrapper {
1174 float:left;
1175 float:left;
1175 }
1176 }
1176
1177
1177 .branding {
1178 .branding {
1178 float: left;
1179 float: left;
1179 padding: 9px 2px;
1180 padding: 9px 2px;
1180 line-height: 1em;
1181 line-height: 1em;
1181 font-size: @navigation-fontsize;
1182 font-size: @navigation-fontsize;
1182
1183
1183 a {
1184 a {
1184 color: @grey5
1185 color: @grey5
1185 }
1186 }
1186
1187
1187 // 1024px or smaller
1188 // 1024px or smaller
1188 @media screen and (max-width: 1180px) {
1189 @media screen and (max-width: 1180px) {
1189 display: none;
1190 display: none;
1190 }
1191 }
1191
1192
1192 }
1193 }
1193
1194
1194 img {
1195 img {
1195 border: none;
1196 border: none;
1196 outline: none;
1197 outline: none;
1197 }
1198 }
1198 user-profile-header
1199 user-profile-header
1199 label {
1200 label {
1200
1201
1201 input[type="checkbox"] {
1202 input[type="checkbox"] {
1202 margin-right: 1em;
1203 margin-right: 1em;
1203 }
1204 }
1204 input[type="radio"] {
1205 input[type="radio"] {
1205 margin-right: 1em;
1206 margin-right: 1em;
1206 }
1207 }
1207 }
1208 }
1208
1209
1209 .review-status {
1210 .review-status {
1210 &.under_review {
1211 &.under_review {
1211 color: @alert3;
1212 color: @alert3;
1212 }
1213 }
1213 &.approved {
1214 &.approved {
1214 color: @alert1;
1215 color: @alert1;
1215 }
1216 }
1216 &.rejected,
1217 &.rejected,
1217 &.forced_closed{
1218 &.forced_closed{
1218 color: @alert2;
1219 color: @alert2;
1219 }
1220 }
1220 &.not_reviewed {
1221 &.not_reviewed {
1221 color: @grey5;
1222 color: @grey5;
1222 }
1223 }
1223 }
1224 }
1224
1225
1225 .review-status-under_review {
1226 .review-status-under_review {
1226 color: @alert3;
1227 color: @alert3;
1227 }
1228 }
1228 .status-tag-under_review {
1229 .status-tag-under_review {
1229 border-color: @alert3;
1230 border-color: @alert3;
1230 }
1231 }
1231
1232
1232 .review-status-approved {
1233 .review-status-approved {
1233 color: @alert1;
1234 color: @alert1;
1234 }
1235 }
1235 .status-tag-approved {
1236 .status-tag-approved {
1236 border-color: @alert1;
1237 border-color: @alert1;
1237 }
1238 }
1238
1239
1239 .review-status-rejected,
1240 .review-status-rejected,
1240 .review-status-forced_closed {
1241 .review-status-forced_closed {
1241 color: @alert2;
1242 color: @alert2;
1242 }
1243 }
1243 .status-tag-rejected,
1244 .status-tag-rejected,
1244 .status-tag-forced_closed {
1245 .status-tag-forced_closed {
1245 border-color: @alert2;
1246 border-color: @alert2;
1246 }
1247 }
1247
1248
1248 .review-status-not_reviewed {
1249 .review-status-not_reviewed {
1249 color: @grey5;
1250 color: @grey5;
1250 }
1251 }
1251 .status-tag-not_reviewed {
1252 .status-tag-not_reviewed {
1252 border-color: @grey5;
1253 border-color: @grey5;
1253 }
1254 }
1254
1255
1255 .test_pattern_preview {
1256 .test_pattern_preview {
1256 margin: @space 0;
1257 margin: @space 0;
1257
1258
1258 p {
1259 p {
1259 margin-bottom: 0;
1260 margin-bottom: 0;
1260 border-bottom: @border-thickness solid @border-default-color;
1261 border-bottom: @border-thickness solid @border-default-color;
1261 color: @grey3;
1262 color: @grey3;
1262 }
1263 }
1263
1264
1264 .btn {
1265 .btn {
1265 margin-bottom: @padding;
1266 margin-bottom: @padding;
1266 }
1267 }
1267 }
1268 }
1268 #test_pattern_result {
1269 #test_pattern_result {
1269 display: none;
1270 display: none;
1270 &:extend(pre);
1271 &:extend(pre);
1271 padding: .9em;
1272 padding: .9em;
1272 color: @grey3;
1273 color: @grey3;
1273 background-color: @grey7;
1274 background-color: @grey7;
1274 border-right: @border-thickness solid @border-default-color;
1275 border-right: @border-thickness solid @border-default-color;
1275 border-bottom: @border-thickness solid @border-default-color;
1276 border-bottom: @border-thickness solid @border-default-color;
1276 border-left: @border-thickness solid @border-default-color;
1277 border-left: @border-thickness solid @border-default-color;
1277 }
1278 }
1278
1279
1279 #repo_vcs_settings {
1280 #repo_vcs_settings {
1280 #inherit_overlay_vcs_default {
1281 #inherit_overlay_vcs_default {
1281 display: none;
1282 display: none;
1282 }
1283 }
1283 #inherit_overlay_vcs_custom {
1284 #inherit_overlay_vcs_custom {
1284 display: custom;
1285 display: custom;
1285 }
1286 }
1286 &.inherited {
1287 &.inherited {
1287 #inherit_overlay_vcs_default {
1288 #inherit_overlay_vcs_default {
1288 display: block;
1289 display: block;
1289 }
1290 }
1290 #inherit_overlay_vcs_custom {
1291 #inherit_overlay_vcs_custom {
1291 display: none;
1292 display: none;
1292 }
1293 }
1293 }
1294 }
1294 }
1295 }
1295
1296
1296 .issue-tracker-link {
1297 .issue-tracker-link {
1297 color: @rcblue;
1298 color: @rcblue;
1298 }
1299 }
1299
1300
1300 // Issue Tracker Table Show/Hide
1301 // Issue Tracker Table Show/Hide
1301 #repo_issue_tracker {
1302 #repo_issue_tracker {
1302 #inherit_overlay {
1303 #inherit_overlay {
1303 display: none;
1304 display: none;
1304 }
1305 }
1305 #custom_overlay {
1306 #custom_overlay {
1306 display: custom;
1307 display: custom;
1307 }
1308 }
1308 &.inherited {
1309 &.inherited {
1309 #inherit_overlay {
1310 #inherit_overlay {
1310 display: block;
1311 display: block;
1311 }
1312 }
1312 #custom_overlay {
1313 #custom_overlay {
1313 display: none;
1314 display: none;
1314 }
1315 }
1315 }
1316 }
1316 }
1317 }
1317 table.issuetracker {
1318 table.issuetracker {
1318 &.readonly {
1319 &.readonly {
1319 tr, td {
1320 tr, td {
1320 color: @grey3;
1321 color: @grey3;
1321 }
1322 }
1322 }
1323 }
1323 .edit {
1324 .edit {
1324 display: none;
1325 display: none;
1325 }
1326 }
1326 .editopen {
1327 .editopen {
1327 .edit {
1328 .edit {
1328 display: inline;
1329 display: inline;
1329 }
1330 }
1330 .entry {
1331 .entry {
1331 display: none;
1332 display: none;
1332 }
1333 }
1333 }
1334 }
1334 tr td.td-action {
1335 tr td.td-action {
1335 min-width: 117px;
1336 min-width: 117px;
1336 }
1337 }
1337 td input {
1338 td input {
1338 max-width: none;
1339 max-width: none;
1339 min-width: 30px;
1340 min-width: 30px;
1340 width: 80%;
1341 width: 80%;
1341 }
1342 }
1342 .issuetracker_pref input {
1343 .issuetracker_pref input {
1343 width: 40%;
1344 width: 40%;
1344 }
1345 }
1345 input.edit_issuetracker_update {
1346 input.edit_issuetracker_update {
1346 margin-right: 0;
1347 margin-right: 0;
1347 width: auto;
1348 width: auto;
1348 }
1349 }
1349 }
1350 }
1350
1351
1351 table.integrations {
1352 table.integrations {
1352 .td-icon {
1353 .td-icon {
1353 width: 20px;
1354 width: 20px;
1354 .integration-icon {
1355 .integration-icon {
1355 height: 20px;
1356 height: 20px;
1356 width: 20px;
1357 width: 20px;
1357 }
1358 }
1358 }
1359 }
1359 }
1360 }
1360
1361
1361 .integrations {
1362 .integrations {
1362 a.integration-box {
1363 a.integration-box {
1363 color: @text-color;
1364 color: @text-color;
1364 &:hover {
1365 &:hover {
1365 .panel {
1366 .panel {
1366 background: #fbfbfb;
1367 background: #fbfbfb;
1367 }
1368 }
1368 }
1369 }
1369 .integration-icon {
1370 .integration-icon {
1370 width: 30px;
1371 width: 30px;
1371 height: 30px;
1372 height: 30px;
1372 margin-right: 20px;
1373 margin-right: 20px;
1373 float: left;
1374 float: left;
1374 }
1375 }
1375
1376
1376 .panel-body {
1377 .panel-body {
1377 padding: 10px;
1378 padding: 10px;
1378 }
1379 }
1379 .panel {
1380 .panel {
1380 margin-bottom: 10px;
1381 margin-bottom: 10px;
1381 }
1382 }
1382 h2 {
1383 h2 {
1383 display: inline-block;
1384 display: inline-block;
1384 margin: 0;
1385 margin: 0;
1385 min-width: 140px;
1386 min-width: 140px;
1386 }
1387 }
1387 }
1388 }
1388 a.integration-box.dummy-integration {
1389 a.integration-box.dummy-integration {
1389 color: @grey4
1390 color: @grey4
1390 }
1391 }
1391 }
1392 }
1392
1393
1393 //Permissions Settings
1394 //Permissions Settings
1394 #add_perm {
1395 #add_perm {
1395 margin: 0 0 @padding;
1396 margin: 0 0 @padding;
1396 cursor: pointer;
1397 cursor: pointer;
1397 }
1398 }
1398
1399
1399 .perm_ac {
1400 .perm_ac {
1400 input {
1401 input {
1401 width: 95%;
1402 width: 95%;
1402 }
1403 }
1403 }
1404 }
1404
1405
1405 .autocomplete-suggestions {
1406 .autocomplete-suggestions {
1406 width: auto !important; // overrides autocomplete.js
1407 width: auto !important; // overrides autocomplete.js
1407 min-width: 278px;
1408 min-width: 278px;
1408 margin: 0;
1409 margin: 0;
1409 border: @border-thickness solid @grey5;
1410 border: @border-thickness solid @grey5;
1410 border-radius: @border-radius;
1411 border-radius: @border-radius;
1411 color: @grey2;
1412 color: @grey2;
1412 background-color: white;
1413 background-color: white;
1413 }
1414 }
1414
1415
1415 .autocomplete-qfilter-suggestions {
1416 .autocomplete-qfilter-suggestions {
1416 width: auto !important; // overrides autocomplete.js
1417 width: auto !important; // overrides autocomplete.js
1417 max-height: 100% !important;
1418 max-height: 100% !important;
1418 min-width: 376px;
1419 min-width: 376px;
1419 margin: 0;
1420 margin: 0;
1420 border: @border-thickness solid @grey5;
1421 border: @border-thickness solid @grey5;
1421 color: @grey2;
1422 color: @grey2;
1422 background-color: white;
1423 background-color: white;
1423 }
1424 }
1424
1425
1425 .autocomplete-selected {
1426 .autocomplete-selected {
1426 background: #F0F0F0;
1427 background: #F0F0F0;
1427 }
1428 }
1428
1429
1429 .ac-container-wrap {
1430 .ac-container-wrap {
1430 margin: 0;
1431 margin: 0;
1431 padding: 8px;
1432 padding: 8px;
1432 border-bottom: @border-thickness solid @grey5;
1433 border-bottom: @border-thickness solid @grey5;
1433 list-style-type: none;
1434 list-style-type: none;
1434 cursor: pointer;
1435 cursor: pointer;
1435
1436
1436 &:hover {
1437 &:hover {
1437 background-color: @grey7;
1438 background-color: @grey7;
1438 }
1439 }
1439
1440
1440 img {
1441 img {
1441 height: @gravatar-size;
1442 height: @gravatar-size;
1442 width: @gravatar-size;
1443 width: @gravatar-size;
1443 margin-right: 1em;
1444 margin-right: 1em;
1444 }
1445 }
1445
1446
1446 strong {
1447 strong {
1447 font-weight: normal;
1448 font-weight: normal;
1448 }
1449 }
1449 }
1450 }
1450
1451
1451 // Settings Dropdown
1452 // Settings Dropdown
1452 .user-menu .container {
1453 .user-menu .container {
1453 padding: 0 4px;
1454 padding: 0 4px;
1454 margin: 0;
1455 margin: 0;
1455 }
1456 }
1456
1457
1457 .user-menu .gravatar {
1458 .user-menu .gravatar {
1458 cursor: pointer;
1459 cursor: pointer;
1459 }
1460 }
1460
1461
1461 .codeblock {
1462 .codeblock {
1462 margin-bottom: @padding;
1463 margin-bottom: @padding;
1463 clear: both;
1464 clear: both;
1464
1465
1465 .stats {
1466 .stats {
1466 overflow: hidden;
1467 overflow: hidden;
1467 }
1468 }
1468
1469
1469 .message{
1470 .message{
1470 textarea{
1471 textarea{
1471 margin: 0;
1472 margin: 0;
1472 }
1473 }
1473 }
1474 }
1474
1475
1475 .code-header {
1476 .code-header {
1476 .stats {
1477 .stats {
1477 line-height: 2em;
1478 line-height: 2em;
1478
1479
1479 .revision_id {
1480 .revision_id {
1480 margin-left: 0;
1481 margin-left: 0;
1481 }
1482 }
1482 .buttons {
1483 .buttons {
1483 padding-right: 0;
1484 padding-right: 0;
1484 }
1485 }
1485 }
1486 }
1486
1487
1487 .item{
1488 .item{
1488 margin-right: 0.5em;
1489 margin-right: 0.5em;
1489 }
1490 }
1490 }
1491 }
1491
1492
1492 #editor_container {
1493 #editor_container {
1493 position: relative;
1494 position: relative;
1494 margin: @padding 10px;
1495 margin: @padding 10px;
1495 }
1496 }
1496 }
1497 }
1497
1498
1498 #file_history_container {
1499 #file_history_container {
1499 display: none;
1500 display: none;
1500 }
1501 }
1501
1502
1502 .file-history-inner {
1503 .file-history-inner {
1503 margin-bottom: 10px;
1504 margin-bottom: 10px;
1504 }
1505 }
1505
1506
1506 // Pull Requests
1507 // Pull Requests
1507 .summary-details {
1508 .summary-details {
1508 width: 100%;
1509 width: 100%;
1509 }
1510 }
1510 .pr-summary {
1511 .pr-summary {
1511 border-bottom: @border-thickness solid @grey5;
1512 border-bottom: @border-thickness solid @grey5;
1512 margin-bottom: @space;
1513 margin-bottom: @space;
1513 }
1514 }
1514
1515
1515 .reviewers {
1516 .reviewers {
1516 width: 98%;
1517 width: 98%;
1517 }
1518 }
1518
1519
1519 .reviewers ul li {
1520 .reviewers ul li {
1520 position: relative;
1521 position: relative;
1521 width: 100%;
1522 width: 100%;
1522 padding-bottom: 8px;
1523 padding-bottom: 8px;
1523 list-style-type: none;
1524 list-style-type: none;
1524 }
1525 }
1525
1526
1526 .reviewer_entry {
1527 .reviewer_entry {
1527 min-height: 55px;
1528 min-height: 55px;
1528 }
1529 }
1529
1530
1530 .reviewer_reason {
1531 .reviewer_reason {
1531 padding-left: 20px;
1532 padding-left: 20px;
1532 line-height: 1.5em;
1533 line-height: 1.5em;
1533 }
1534 }
1534 .reviewer_status {
1535 .reviewer_status {
1535 display: inline-block;
1536 display: inline-block;
1536 width: 20px;
1537 width: 20px;
1537 min-width: 20px;
1538 min-width: 20px;
1538 height: 1.2em;
1539 height: 1.2em;
1539 line-height: 1em;
1540 line-height: 1em;
1540 }
1541 }
1541
1542
1542 .reviewer_name {
1543 .reviewer_name {
1543 display: inline-block;
1544 display: inline-block;
1544 max-width: 83%;
1545 max-width: 83%;
1545 padding-right: 20px;
1546 padding-right: 20px;
1546 vertical-align: middle;
1547 vertical-align: middle;
1547 line-height: 1;
1548 line-height: 1;
1548
1549
1549 .rc-user {
1550 .rc-user {
1550 min-width: 0;
1551 min-width: 0;
1551 margin: -2px 1em 0 0;
1552 margin: -2px 1em 0 0;
1552 }
1553 }
1553
1554
1554 .reviewer {
1555 .reviewer {
1555 float: left;
1556 float: left;
1556 }
1557 }
1557 }
1558 }
1558
1559
1559 .reviewer_member_mandatory {
1560 .reviewer_member_mandatory {
1560 width: 16px;
1561 width: 16px;
1561 font-size: 11px;
1562 font-size: 11px;
1562 margin: 0;
1563 margin: 0;
1563 padding: 0;
1564 padding: 0;
1564 color: black;
1565 color: black;
1565 opacity: 0.4;
1566 opacity: 0.4;
1566 }
1567 }
1567
1568
1568 .reviewer_member_mandatory_remove,
1569 .reviewer_member_mandatory_remove,
1569 .reviewer_member_remove {
1570 .reviewer_member_remove {
1570 width: 16px;
1571 width: 16px;
1571 padding: 0;
1572 padding: 0;
1572 color: black;
1573 color: black;
1573 cursor: pointer;
1574 cursor: pointer;
1574 }
1575 }
1575
1576
1576 .reviewer_member_mandatory_remove {
1577 .reviewer_member_mandatory_remove {
1577 color: @grey4;
1578 color: @grey4;
1578 }
1579 }
1579
1580
1580 .reviewer_member_status {
1581 .reviewer_member_status {
1581 margin-top: 5px;
1582 margin-top: 5px;
1582 }
1583 }
1583 .pr-summary #summary{
1584 .pr-summary #summary{
1584 width: 100%;
1585 width: 100%;
1585 }
1586 }
1586 .pr-summary .action_button:hover {
1587 .pr-summary .action_button:hover {
1587 border: 0;
1588 border: 0;
1588 cursor: pointer;
1589 cursor: pointer;
1589 }
1590 }
1590 .pr-details-title {
1591 .pr-details-title {
1591 height: 20px;
1592 height: 20px;
1592 line-height: 20px;
1593 line-height: 20px;
1593
1594
1594 padding-bottom: 8px;
1595 padding-bottom: 8px;
1595 border-bottom: @border-thickness solid @grey5;
1596 border-bottom: @border-thickness solid @grey5;
1596
1597
1597 .action_button.disabled {
1598 .action_button.disabled {
1598 color: @grey4;
1599 color: @grey4;
1599 cursor: inherit;
1600 cursor: inherit;
1600 }
1601 }
1601 .action_button {
1602 .action_button {
1602 color: @rcblue;
1603 color: @rcblue;
1603 }
1604 }
1604 }
1605 }
1605 .pr-details-content {
1606 .pr-details-content {
1606 margin-top: @textmargin - 5;
1607 margin-top: @textmargin - 5;
1607 margin-bottom: @textmargin - 5;
1608 margin-bottom: @textmargin - 5;
1608 }
1609 }
1609
1610
1610 .pr-reviewer-rules {
1611 .pr-reviewer-rules {
1611 padding: 10px 0px 20px 0px;
1612 padding: 10px 0px 20px 0px;
1612 }
1613 }
1613
1614
1614 .todo-resolved {
1615 .todo-resolved {
1615 text-decoration: line-through;
1616 text-decoration: line-through;
1616 }
1617 }
1617
1618
1618 .todo-table, .comments-table {
1619 .todo-table, .comments-table {
1619 width: 100%;
1620 width: 100%;
1620
1621
1621 td {
1622 td {
1622 padding: 5px 0px;
1623 padding: 5px 0px;
1623 }
1624 }
1624
1625
1625 .td-todo-number {
1626 .td-todo-number {
1626 text-align: left;
1627 text-align: left;
1627 white-space: nowrap;
1628 white-space: nowrap;
1628 width: 1%;
1629 width: 1%;
1629 padding-right: 2px;
1630 padding-right: 2px;
1630 }
1631 }
1631
1632
1632 .td-todo-gravatar {
1633 .td-todo-gravatar {
1633 width: 5%;
1634 width: 5%;
1634
1635
1635 img {
1636 img {
1636 margin: -3px 0;
1637 margin: -3px 0;
1637 }
1638 }
1638 }
1639 }
1639
1640
1640 }
1641 }
1641
1642
1642 .todo-comment-text-wrapper {
1643 .todo-comment-text-wrapper {
1643 display: inline-grid;
1644 display: inline-grid;
1644 }
1645 }
1645
1646
1646 .todo-comment-text {
1647 .todo-comment-text {
1647 margin-left: 5px;
1648 margin-left: 5px;
1648 white-space: nowrap;
1649 white-space: nowrap;
1649 overflow: hidden;
1650 overflow: hidden;
1650 text-overflow: ellipsis;
1651 text-overflow: ellipsis;
1651 }
1652 }
1652
1653
1653 table.group_members {
1654 table.group_members {
1654 width: 100%
1655 width: 100%
1655 }
1656 }
1656
1657
1657 .group_members {
1658 .group_members {
1658 margin-top: 0;
1659 margin-top: 0;
1659 padding: 0;
1660 padding: 0;
1660
1661
1661 img {
1662 img {
1662 height: @gravatar-size;
1663 height: @gravatar-size;
1663 width: @gravatar-size;
1664 width: @gravatar-size;
1664 margin-right: .5em;
1665 margin-right: .5em;
1665 margin-left: 3px;
1666 margin-left: 3px;
1666 }
1667 }
1667
1668
1668 .to-delete {
1669 .to-delete {
1669 .user {
1670 .user {
1670 text-decoration: line-through;
1671 text-decoration: line-through;
1671 }
1672 }
1672 }
1673 }
1673 }
1674 }
1674
1675
1675 .compare_view_commits_title {
1676 .compare_view_commits_title {
1676 .disabled {
1677 .disabled {
1677 cursor: inherit;
1678 cursor: inherit;
1678 &:hover{
1679 &:hover{
1679 background-color: inherit;
1680 background-color: inherit;
1680 color: inherit;
1681 color: inherit;
1681 }
1682 }
1682 }
1683 }
1683 }
1684 }
1684
1685
1685 .subtitle-compare {
1686 .subtitle-compare {
1686 margin: -15px 0px 0px 0px;
1687 margin: -15px 0px 0px 0px;
1687 }
1688 }
1688
1689
1689 // new entry in group_members
1690 // new entry in group_members
1690 .td-author-new-entry {
1691 .td-author-new-entry {
1691 background-color: rgba(red(@alert1), green(@alert1), blue(@alert1), 0.3);
1692 background-color: rgba(red(@alert1), green(@alert1), blue(@alert1), 0.3);
1692 }
1693 }
1693
1694
1694 .usergroup_member_remove {
1695 .usergroup_member_remove {
1695 width: 16px;
1696 width: 16px;
1696 margin-bottom: 10px;
1697 margin-bottom: 10px;
1697 padding: 0;
1698 padding: 0;
1698 color: black !important;
1699 color: black !important;
1699 cursor: pointer;
1700 cursor: pointer;
1700 }
1701 }
1701
1702
1702 .reviewer_ac .ac-input {
1703 .reviewer_ac .ac-input {
1703 width: 98%;
1704 width: 98%;
1704 margin-bottom: 1em;
1705 margin-bottom: 1em;
1705 }
1706 }
1706
1707
1707 .observer_ac .ac-input {
1708 .observer_ac .ac-input {
1708 width: 98%;
1709 width: 98%;
1709 margin-bottom: 1em;
1710 margin-bottom: 1em;
1710 }
1711 }
1711
1712
1712 .rule-table {
1713 .rule-table {
1713 width: 100%;
1714 width: 100%;
1714 }
1715 }
1715
1716
1716 .rule-table td {
1717 .rule-table td {
1717
1718
1718 }
1719 }
1719
1720
1720 .rule-table .td-role {
1721 .rule-table .td-role {
1721 width: 100px
1722 width: 100px
1722 }
1723 }
1723
1724
1724 .rule-table .td-mandatory {
1725 .rule-table .td-mandatory {
1725 width: 100px
1726 width: 100px
1726 }
1727 }
1727
1728
1728 .rule-table .td-group-votes {
1729 .rule-table .td-group-votes {
1729 width: 150px
1730 width: 150px
1730 }
1731 }
1731
1732
1732 .compare_view_commits tr{
1733 .compare_view_commits tr{
1733 height: 20px;
1734 height: 20px;
1734 }
1735 }
1735 .compare_view_commits td {
1736 .compare_view_commits td {
1736 vertical-align: top;
1737 vertical-align: top;
1737 padding-top: 10px;
1738 padding-top: 10px;
1738 }
1739 }
1739 .compare_view_commits .author {
1740 .compare_view_commits .author {
1740 margin-left: 5px;
1741 margin-left: 5px;
1741 }
1742 }
1742
1743
1743 .compare_view_commits {
1744 .compare_view_commits {
1744 .color-a {
1745 .color-a {
1745 color: @alert1;
1746 color: @alert1;
1746 }
1747 }
1747
1748
1748 .color-c {
1749 .color-c {
1749 color: @color3;
1750 color: @color3;
1750 }
1751 }
1751
1752
1752 .color-r {
1753 .color-r {
1753 color: @color5;
1754 color: @color5;
1754 }
1755 }
1755
1756
1756 .color-a-bg {
1757 .color-a-bg {
1757 background-color: @alert1;
1758 background-color: @alert1;
1758 }
1759 }
1759
1760
1760 .color-c-bg {
1761 .color-c-bg {
1761 background-color: @alert3;
1762 background-color: @alert3;
1762 }
1763 }
1763
1764
1764 .color-r-bg {
1765 .color-r-bg {
1765 background-color: @alert2;
1766 background-color: @alert2;
1766 }
1767 }
1767
1768
1768 .color-a-border {
1769 .color-a-border {
1769 border: 1px solid @alert1;
1770 border: 1px solid @alert1;
1770 }
1771 }
1771
1772
1772 .color-c-border {
1773 .color-c-border {
1773 border: 1px solid @alert3;
1774 border: 1px solid @alert3;
1774 }
1775 }
1775
1776
1776 .color-r-border {
1777 .color-r-border {
1777 border: 1px solid @alert2;
1778 border: 1px solid @alert2;
1778 }
1779 }
1779
1780
1780 .commit-change-indicator {
1781 .commit-change-indicator {
1781 width: 15px;
1782 width: 15px;
1782 height: 15px;
1783 height: 15px;
1783 position: relative;
1784 position: relative;
1784 left: 15px;
1785 left: 15px;
1785 }
1786 }
1786
1787
1787 .commit-change-content {
1788 .commit-change-content {
1788 text-align: center;
1789 text-align: center;
1789 vertical-align: middle;
1790 vertical-align: middle;
1790 line-height: 15px;
1791 line-height: 15px;
1791 }
1792 }
1792 }
1793 }
1793
1794
1794 .compare_view_filepath {
1795 .compare_view_filepath {
1795 color: @grey1;
1796 color: @grey1;
1796 }
1797 }
1797
1798
1798 .show_more {
1799 .show_more {
1799 display: inline-block;
1800 display: inline-block;
1800 width: 0;
1801 width: 0;
1801 height: 0;
1802 height: 0;
1802 vertical-align: middle;
1803 vertical-align: middle;
1803 content: "";
1804 content: "";
1804 border: 4px solid;
1805 border: 4px solid;
1805 border-right-color: transparent;
1806 border-right-color: transparent;
1806 border-bottom-color: transparent;
1807 border-bottom-color: transparent;
1807 border-left-color: transparent;
1808 border-left-color: transparent;
1808 font-size: 0;
1809 font-size: 0;
1809 }
1810 }
1810
1811
1811 .journal_more .show_more {
1812 .journal_more .show_more {
1812 display: inline;
1813 display: inline;
1813
1814
1814 &:after {
1815 &:after {
1815 content: none;
1816 content: none;
1816 }
1817 }
1817 }
1818 }
1818
1819
1819 .compare_view_commits .collapse_commit:after {
1820 .compare_view_commits .collapse_commit:after {
1820 cursor: pointer;
1821 cursor: pointer;
1821 content: "\00A0\25B4";
1822 content: "\00A0\25B4";
1822 margin-left: -3px;
1823 margin-left: -3px;
1823 font-size: 17px;
1824 font-size: 17px;
1824 color: @grey4;
1825 color: @grey4;
1825 }
1826 }
1826
1827
1827 .diff_links {
1828 .diff_links {
1828 margin-left: 8px;
1829 margin-left: 8px;
1829 }
1830 }
1830
1831
1831 #pull_request_overview {
1832 #pull_request_overview {
1832 div.ancestor {
1833 div.ancestor {
1833 margin: -33px 0;
1834 margin: -33px 0;
1834 }
1835 }
1835 }
1836 }
1836
1837
1837 div.ancestor {
1838 div.ancestor {
1838
1839
1839 }
1840 }
1840
1841
1841 .cs_icon_td input[type="checkbox"] {
1842 .cs_icon_td input[type="checkbox"] {
1842 display: none;
1843 display: none;
1843 }
1844 }
1844
1845
1845 .cs_icon_td .expand_file_icon:after {
1846 .cs_icon_td .expand_file_icon:after {
1846 cursor: pointer;
1847 cursor: pointer;
1847 content: "\00A0\25B6";
1848 content: "\00A0\25B6";
1848 font-size: 12px;
1849 font-size: 12px;
1849 color: @grey4;
1850 color: @grey4;
1850 }
1851 }
1851
1852
1852 .cs_icon_td .collapse_file_icon:after {
1853 .cs_icon_td .collapse_file_icon:after {
1853 cursor: pointer;
1854 cursor: pointer;
1854 content: "\00A0\25BC";
1855 content: "\00A0\25BC";
1855 font-size: 12px;
1856 font-size: 12px;
1856 color: @grey4;
1857 color: @grey4;
1857 }
1858 }
1858
1859
1859 /*new binary
1860 /*new binary
1860 NEW_FILENODE = 1
1861 NEW_FILENODE = 1
1861 DEL_FILENODE = 2
1862 DEL_FILENODE = 2
1862 MOD_FILENODE = 3
1863 MOD_FILENODE = 3
1863 RENAMED_FILENODE = 4
1864 RENAMED_FILENODE = 4
1864 COPIED_FILENODE = 5
1865 COPIED_FILENODE = 5
1865 CHMOD_FILENODE = 6
1866 CHMOD_FILENODE = 6
1866 BIN_FILENODE = 7
1867 BIN_FILENODE = 7
1867 */
1868 */
1868 .cs_files_expand {
1869 .cs_files_expand {
1869 font-size: @basefontsize + 5px;
1870 font-size: @basefontsize + 5px;
1870 line-height: 1.8em;
1871 line-height: 1.8em;
1871 float: right;
1872 float: right;
1872 }
1873 }
1873
1874
1874 .cs_files_expand span{
1875 .cs_files_expand span{
1875 color: @rcblue;
1876 color: @rcblue;
1876 cursor: pointer;
1877 cursor: pointer;
1877 }
1878 }
1878 .cs_files {
1879 .cs_files {
1879 clear: both;
1880 clear: both;
1880 padding-bottom: @padding;
1881 padding-bottom: @padding;
1881
1882
1882 .cur_cs {
1883 .cur_cs {
1883 margin: 10px 2px;
1884 margin: 10px 2px;
1884 font-weight: bold;
1885 font-weight: bold;
1885 }
1886 }
1886
1887
1887 .node {
1888 .node {
1888 float: left;
1889 float: left;
1889 }
1890 }
1890
1891
1891 .changes {
1892 .changes {
1892 float: right;
1893 float: right;
1893 color: white;
1894 color: white;
1894 font-size: @basefontsize - 4px;
1895 font-size: @basefontsize - 4px;
1895 margin-top: 4px;
1896 margin-top: 4px;
1896 opacity: 0.6;
1897 opacity: 0.6;
1897 filter: Alpha(opacity=60); /* IE8 and earlier */
1898 filter: Alpha(opacity=60); /* IE8 and earlier */
1898
1899
1899 .added {
1900 .added {
1900 background-color: @alert1;
1901 background-color: @alert1;
1901 float: left;
1902 float: left;
1902 text-align: center;
1903 text-align: center;
1903 }
1904 }
1904
1905
1905 .deleted {
1906 .deleted {
1906 background-color: @alert2;
1907 background-color: @alert2;
1907 float: left;
1908 float: left;
1908 text-align: center;
1909 text-align: center;
1909 }
1910 }
1910
1911
1911 .bin {
1912 .bin {
1912 background-color: @alert1;
1913 background-color: @alert1;
1913 text-align: center;
1914 text-align: center;
1914 }
1915 }
1915
1916
1916 /*new binary*/
1917 /*new binary*/
1917 .bin.bin1 {
1918 .bin.bin1 {
1918 background-color: @alert1;
1919 background-color: @alert1;
1919 text-align: center;
1920 text-align: center;
1920 }
1921 }
1921
1922
1922 /*deleted binary*/
1923 /*deleted binary*/
1923 .bin.bin2 {
1924 .bin.bin2 {
1924 background-color: @alert2;
1925 background-color: @alert2;
1925 text-align: center;
1926 text-align: center;
1926 }
1927 }
1927
1928
1928 /*mod binary*/
1929 /*mod binary*/
1929 .bin.bin3 {
1930 .bin.bin3 {
1930 background-color: @grey2;
1931 background-color: @grey2;
1931 text-align: center;
1932 text-align: center;
1932 }
1933 }
1933
1934
1934 /*rename file*/
1935 /*rename file*/
1935 .bin.bin4 {
1936 .bin.bin4 {
1936 background-color: @alert4;
1937 background-color: @alert4;
1937 text-align: center;
1938 text-align: center;
1938 }
1939 }
1939
1940
1940 /*copied file*/
1941 /*copied file*/
1941 .bin.bin5 {
1942 .bin.bin5 {
1942 background-color: @alert4;
1943 background-color: @alert4;
1943 text-align: center;
1944 text-align: center;
1944 }
1945 }
1945
1946
1946 /*chmod file*/
1947 /*chmod file*/
1947 .bin.bin6 {
1948 .bin.bin6 {
1948 background-color: @grey2;
1949 background-color: @grey2;
1949 text-align: center;
1950 text-align: center;
1950 }
1951 }
1951 }
1952 }
1952 }
1953 }
1953
1954
1954 .cs_files .cs_added, .cs_files .cs_A,
1955 .cs_files .cs_added, .cs_files .cs_A,
1955 .cs_files .cs_added, .cs_files .cs_M,
1956 .cs_files .cs_added, .cs_files .cs_M,
1956 .cs_files .cs_added, .cs_files .cs_D {
1957 .cs_files .cs_added, .cs_files .cs_D {
1957 height: 16px;
1958 height: 16px;
1958 padding-right: 10px;
1959 padding-right: 10px;
1959 margin-top: 7px;
1960 margin-top: 7px;
1960 text-align: left;
1961 text-align: left;
1961 }
1962 }
1962
1963
1963 .cs_icon_td {
1964 .cs_icon_td {
1964 min-width: 16px;
1965 min-width: 16px;
1965 width: 16px;
1966 width: 16px;
1966 }
1967 }
1967
1968
1968 .pull-request-merge {
1969 .pull-request-merge {
1969 border: 1px solid @grey5;
1970 border: 1px solid @grey5;
1970 padding: 10px 0px 20px;
1971 padding: 10px 0px 20px;
1971 margin-top: 10px;
1972 margin-top: 10px;
1972 margin-bottom: 20px;
1973 margin-bottom: 20px;
1973 }
1974 }
1974
1975
1975 .pull-request-merge-refresh {
1976 .pull-request-merge-refresh {
1976 margin: 2px 7px;
1977 margin: 2px 7px;
1977 a {
1978 a {
1978 color: @grey3;
1979 color: @grey3;
1979 }
1980 }
1980 }
1981 }
1981
1982
1982 .pull-request-merge ul {
1983 .pull-request-merge ul {
1983 padding: 0px 0px;
1984 padding: 0px 0px;
1984 }
1985 }
1985
1986
1986 .pull-request-merge li {
1987 .pull-request-merge li {
1987 list-style-type: none;
1988 list-style-type: none;
1988 }
1989 }
1989
1990
1990 .pull-request-merge .pull-request-wrap {
1991 .pull-request-merge .pull-request-wrap {
1991 height: auto;
1992 height: auto;
1992 padding: 0px 0px;
1993 padding: 0px 0px;
1993 text-align: right;
1994 text-align: right;
1994 }
1995 }
1995
1996
1996 .pull-request-merge span {
1997 .pull-request-merge span {
1997 margin-right: 5px;
1998 margin-right: 5px;
1998 }
1999 }
1999
2000
2000 .pull-request-merge-actions {
2001 .pull-request-merge-actions {
2001 min-height: 30px;
2002 min-height: 30px;
2002 padding: 0px 0px;
2003 padding: 0px 0px;
2003 }
2004 }
2004
2005
2005 .pull-request-merge-info {
2006 .pull-request-merge-info {
2006 padding: 0px 5px 5px 0px;
2007 padding: 0px 5px 5px 0px;
2007 }
2008 }
2008
2009
2009 .merge-status {
2010 .merge-status {
2010 margin-right: 5px;
2011 margin-right: 5px;
2011 }
2012 }
2012
2013
2013 .merge-message {
2014 .merge-message {
2014 font-size: 1.2em
2015 font-size: 1.2em
2015 }
2016 }
2016
2017
2017 .merge-message.success i,
2018 .merge-message.success i,
2018 .merge-icon.success i {
2019 .merge-icon.success i {
2019 color:@alert1;
2020 color:@alert1;
2020 }
2021 }
2021
2022
2022 .merge-message.warning i,
2023 .merge-message.warning i,
2023 .merge-icon.warning i {
2024 .merge-icon.warning i {
2024 color: @alert3;
2025 color: @alert3;
2025 }
2026 }
2026
2027
2027 .merge-message.error i,
2028 .merge-message.error i,
2028 .merge-icon.error i {
2029 .merge-icon.error i {
2029 color:@alert2;
2030 color:@alert2;
2030 }
2031 }
2031
2032
2032 .pr-versions {
2033 .pr-versions {
2033 font-size: 1.1em;
2034 font-size: 1.1em;
2034 padding: 7.5px;
2035 padding: 7.5px;
2035
2036
2036 table {
2037 table {
2037
2038
2038 }
2039 }
2039
2040
2040 td {
2041 td {
2041 line-height: 15px;
2042 line-height: 15px;
2042 }
2043 }
2043
2044
2044 .compare-radio-button {
2045 .compare-radio-button {
2045 position: relative;
2046 position: relative;
2046 top: -3px;
2047 top: -3px;
2047 }
2048 }
2048 }
2049 }
2049
2050
2050
2051
2051 #close_pull_request {
2052 #close_pull_request {
2052 margin-right: 0px;
2053 margin-right: 0px;
2053 }
2054 }
2054
2055
2055 .empty_data {
2056 .empty_data {
2056 color: @grey4;
2057 color: @grey4;
2057 }
2058 }
2058
2059
2059 #changeset_compare_view_content {
2060 #changeset_compare_view_content {
2060 clear: both;
2061 clear: both;
2061 width: 100%;
2062 width: 100%;
2062 box-sizing: border-box;
2063 box-sizing: border-box;
2063 .border-radius(@border-radius);
2064 .border-radius(@border-radius);
2064
2065
2065 .help-block {
2066 .help-block {
2066 margin: @padding 0;
2067 margin: @padding 0;
2067 color: @text-color;
2068 color: @text-color;
2068 &.pre-formatting {
2069 &.pre-formatting {
2069 white-space: pre;
2070 white-space: pre;
2070 }
2071 }
2071 }
2072 }
2072
2073
2073 .empty_data {
2074 .empty_data {
2074 margin: @padding 0;
2075 margin: @padding 0;
2075 }
2076 }
2076
2077
2077 .alert {
2078 .alert {
2078 margin-bottom: @space;
2079 margin-bottom: @space;
2079 }
2080 }
2080 }
2081 }
2081
2082
2082 .table_disp {
2083 .table_disp {
2083 .status {
2084 .status {
2084 width: auto;
2085 width: auto;
2085 }
2086 }
2086 }
2087 }
2087
2088
2088
2089
2089 .creation_in_progress {
2090 .creation_in_progress {
2090 color: @grey4
2091 color: @grey4
2091 }
2092 }
2092
2093
2093 .status_box_menu {
2094 .status_box_menu {
2094 margin: 0;
2095 margin: 0;
2095 }
2096 }
2096
2097
2097 .notification-table{
2098 .notification-table{
2098 margin-bottom: @space;
2099 margin-bottom: @space;
2099 display: table;
2100 display: table;
2100 width: 100%;
2101 width: 100%;
2101
2102
2102 .container{
2103 .container{
2103 display: table-row;
2104 display: table-row;
2104
2105
2105 .notification-header{
2106 .notification-header{
2106 border-bottom: @border-thickness solid @border-default-color;
2107 border-bottom: @border-thickness solid @border-default-color;
2107 }
2108 }
2108
2109
2109 .notification-subject{
2110 .notification-subject{
2110 display: table-cell;
2111 display: table-cell;
2111 }
2112 }
2112 }
2113 }
2113 }
2114 }
2114
2115
2115 // Notifications
2116 // Notifications
2116 .notification-header{
2117 .notification-header{
2117 display: table;
2118 display: table;
2118 width: 100%;
2119 width: 100%;
2119 padding: floor(@basefontsize/2) 0;
2120 padding: floor(@basefontsize/2) 0;
2120 line-height: 1em;
2121 line-height: 1em;
2121
2122
2122 .desc, .delete-notifications, .read-notifications{
2123 .desc, .delete-notifications, .read-notifications{
2123 display: table-cell;
2124 display: table-cell;
2124 text-align: left;
2125 text-align: left;
2125 }
2126 }
2126
2127
2127 .delete-notifications, .read-notifications{
2128 .delete-notifications, .read-notifications{
2128 width: 35px;
2129 width: 35px;
2129 min-width: 35px; //fixes when only one button is displayed
2130 min-width: 35px; //fixes when only one button is displayed
2130 }
2131 }
2131 }
2132 }
2132
2133
2133 .notification-body {
2134 .notification-body {
2134 .markdown-block,
2135 .markdown-block,
2135 .rst-block {
2136 .rst-block {
2136 padding: @padding 0;
2137 padding: @padding 0;
2137 }
2138 }
2138
2139
2139 .notification-subject {
2140 .notification-subject {
2140 padding: @textmargin 0;
2141 padding: @textmargin 0;
2141 border-bottom: @border-thickness solid @border-default-color;
2142 border-bottom: @border-thickness solid @border-default-color;
2142 }
2143 }
2143 }
2144 }
2144
2145
2145 .notice-messages {
2146 .notice-messages {
2146 .markdown-block,
2147 .markdown-block,
2147 .rst-block {
2148 .rst-block {
2148 padding: 0;
2149 padding: 0;
2149 }
2150 }
2150 }
2151 }
2151
2152
2152 .notifications_buttons{
2153 .notifications_buttons{
2153 float: right;
2154 float: right;
2154 }
2155 }
2155
2156
2156 #notification-status{
2157 #notification-status{
2157 display: inline;
2158 display: inline;
2158 }
2159 }
2159
2160
2160 // Repositories
2161 // Repositories
2161
2162
2162 #summary.fields{
2163 #summary.fields{
2163 display: table;
2164 display: table;
2164
2165
2165 .field{
2166 .field{
2166 display: table-row;
2167 display: table-row;
2167
2168
2168 .label-summary{
2169 .label-summary{
2169 display: table-cell;
2170 display: table-cell;
2170 min-width: @label-summary-minwidth;
2171 min-width: @label-summary-minwidth;
2171 padding-top: @padding/2;
2172 padding-top: @padding/2;
2172 padding-bottom: @padding/2;
2173 padding-bottom: @padding/2;
2173 padding-right: @padding/2;
2174 padding-right: @padding/2;
2174 }
2175 }
2175
2176
2176 .input{
2177 .input{
2177 display: table-cell;
2178 display: table-cell;
2178 padding: @padding/2;
2179 padding: @padding/2;
2179
2180
2180 input{
2181 input{
2181 min-width: 29em;
2182 min-width: 29em;
2182 padding: @padding/4;
2183 padding: @padding/4;
2183 }
2184 }
2184 }
2185 }
2185 .statistics, .downloads{
2186 .statistics, .downloads{
2186 .disabled{
2187 .disabled{
2187 color: @grey4;
2188 color: @grey4;
2188 }
2189 }
2189 }
2190 }
2190 }
2191 }
2191 }
2192 }
2192
2193
2193 #summary{
2194 #summary{
2194 width: 70%;
2195 width: 70%;
2195 }
2196 }
2196
2197
2197
2198
2198 // Journal
2199 // Journal
2199 .journal.title {
2200 .journal.title {
2200 h5 {
2201 h5 {
2201 float: left;
2202 float: left;
2202 margin: 0;
2203 margin: 0;
2203 width: 70%;
2204 width: 70%;
2204 }
2205 }
2205
2206
2206 ul {
2207 ul {
2207 float: right;
2208 float: right;
2208 display: inline-block;
2209 display: inline-block;
2209 margin: 0;
2210 margin: 0;
2210 width: 30%;
2211 width: 30%;
2211 text-align: right;
2212 text-align: right;
2212
2213
2213 li {
2214 li {
2214 display: inline;
2215 display: inline;
2215 font-size: @journal-fontsize;
2216 font-size: @journal-fontsize;
2216 line-height: 1em;
2217 line-height: 1em;
2217
2218
2218 list-style-type: none;
2219 list-style-type: none;
2219 }
2220 }
2220 }
2221 }
2221 }
2222 }
2222
2223
2223 .filterexample {
2224 .filterexample {
2224 position: absolute;
2225 position: absolute;
2225 top: 95px;
2226 top: 95px;
2226 left: @contentpadding;
2227 left: @contentpadding;
2227 color: @rcblue;
2228 color: @rcblue;
2228 font-size: 11px;
2229 font-size: 11px;
2229 font-family: @text-regular;
2230 font-family: @text-regular;
2230 cursor: help;
2231 cursor: help;
2231
2232
2232 &:hover {
2233 &:hover {
2233 color: @rcdarkblue;
2234 color: @rcdarkblue;
2234 }
2235 }
2235
2236
2236 @media (max-width:768px) {
2237 @media (max-width:768px) {
2237 position: relative;
2238 position: relative;
2238 top: auto;
2239 top: auto;
2239 left: auto;
2240 left: auto;
2240 display: block;
2241 display: block;
2241 }
2242 }
2242 }
2243 }
2243
2244
2244
2245
2245 #journal{
2246 #journal{
2246 margin-bottom: @space;
2247 margin-bottom: @space;
2247
2248
2248 .journal_day{
2249 .journal_day{
2249 margin-bottom: @textmargin/2;
2250 margin-bottom: @textmargin/2;
2250 padding-bottom: @textmargin/2;
2251 padding-bottom: @textmargin/2;
2251 font-size: @journal-fontsize;
2252 font-size: @journal-fontsize;
2252 border-bottom: @border-thickness solid @border-default-color;
2253 border-bottom: @border-thickness solid @border-default-color;
2253 }
2254 }
2254
2255
2255 .journal_container{
2256 .journal_container{
2256 margin-bottom: @space;
2257 margin-bottom: @space;
2257
2258
2258 .journal_user{
2259 .journal_user{
2259 display: inline-block;
2260 display: inline-block;
2260 }
2261 }
2261 .journal_action_container{
2262 .journal_action_container{
2262 display: block;
2263 display: block;
2263 margin-top: @textmargin;
2264 margin-top: @textmargin;
2264
2265
2265 div{
2266 div{
2266 display: inline;
2267 display: inline;
2267 }
2268 }
2268
2269
2269 div.journal_action_params{
2270 div.journal_action_params{
2270 display: block;
2271 display: block;
2271 }
2272 }
2272
2273
2273 div.journal_repo:after{
2274 div.journal_repo:after{
2274 content: "\A";
2275 content: "\A";
2275 white-space: pre;
2276 white-space: pre;
2276 }
2277 }
2277
2278
2278 div.date{
2279 div.date{
2279 display: block;
2280 display: block;
2280 margin-bottom: @textmargin;
2281 margin-bottom: @textmargin;
2281 }
2282 }
2282 }
2283 }
2283 }
2284 }
2284 }
2285 }
2285
2286
2286 // Files
2287 // Files
2287 .edit-file-title {
2288 .edit-file-title {
2288 font-size: 16px;
2289 font-size: 16px;
2289
2290
2290 .title-heading {
2291 .title-heading {
2291 padding: 2px;
2292 padding: 2px;
2292 }
2293 }
2293 }
2294 }
2294
2295
2295 .edit-file-fieldset {
2296 .edit-file-fieldset {
2296 margin: @sidebarpadding 0;
2297 margin: @sidebarpadding 0;
2297
2298
2298 .fieldset {
2299 .fieldset {
2299 .left-label {
2300 .left-label {
2300 width: 13%;
2301 width: 13%;
2301 }
2302 }
2302 .right-content {
2303 .right-content {
2303 width: 87%;
2304 width: 87%;
2304 max-width: 100%;
2305 max-width: 100%;
2305 }
2306 }
2306 .filename-label {
2307 .filename-label {
2307 margin-top: 13px;
2308 margin-top: 13px;
2308 }
2309 }
2309 .commit-message-label {
2310 .commit-message-label {
2310 margin-top: 4px;
2311 margin-top: 4px;
2311 }
2312 }
2312 .file-upload-input {
2313 .file-upload-input {
2313 input {
2314 input {
2314 display: none;
2315 display: none;
2315 }
2316 }
2316 margin-top: 10px;
2317 margin-top: 10px;
2317 }
2318 }
2318 .file-upload-label {
2319 .file-upload-label {
2319 margin-top: 10px;
2320 margin-top: 10px;
2320 }
2321 }
2321 p {
2322 p {
2322 margin-top: 5px;
2323 margin-top: 5px;
2323 }
2324 }
2324
2325
2325 }
2326 }
2326 .custom-path-link {
2327 .custom-path-link {
2327 margin-left: 5px;
2328 margin-left: 5px;
2328 }
2329 }
2329 #commit {
2330 #commit {
2330 resize: vertical;
2331 resize: vertical;
2331 }
2332 }
2332 }
2333 }
2333
2334
2334 .delete-file-preview {
2335 .delete-file-preview {
2335 max-height: 250px;
2336 max-height: 250px;
2336 }
2337 }
2337
2338
2338 .new-file,
2339 .new-file,
2339 #filter_activate,
2340 #filter_activate,
2340 #filter_deactivate {
2341 #filter_deactivate {
2341 float: right;
2342 float: right;
2342 margin: 0 0 0 10px;
2343 margin: 0 0 0 10px;
2343 }
2344 }
2344
2345
2345 .file-upload-transaction-wrapper {
2346 .file-upload-transaction-wrapper {
2346 margin-top: 57px;
2347 margin-top: 57px;
2347 clear: both;
2348 clear: both;
2348 }
2349 }
2349
2350
2350 .file-upload-transaction-wrapper .error {
2351 .file-upload-transaction-wrapper .error {
2351 color: @color5;
2352 color: @color5;
2352 }
2353 }
2353
2354
2354 .file-upload-transaction {
2355 .file-upload-transaction {
2355 min-height: 200px;
2356 min-height: 200px;
2356 padding: 54px;
2357 padding: 54px;
2357 border: 1px solid @grey5;
2358 border: 1px solid @grey5;
2358 text-align: center;
2359 text-align: center;
2359 clear: both;
2360 clear: both;
2360 }
2361 }
2361
2362
2362 .file-upload-transaction i {
2363 .file-upload-transaction i {
2363 font-size: 48px
2364 font-size: 48px
2364 }
2365 }
2365
2366
2366 h3.files_location{
2367 h3.files_location{
2367 line-height: 2.4em;
2368 line-height: 2.4em;
2368 }
2369 }
2369
2370
2370 .browser-nav {
2371 .browser-nav {
2371 width: 100%;
2372 width: 100%;
2372 display: table;
2373 display: table;
2373 margin-bottom: 20px;
2374 margin-bottom: 20px;
2374
2375
2375 .info_box {
2376 .info_box {
2376 float: left;
2377 float: left;
2377 display: inline-table;
2378 display: inline-table;
2378 height: 2.5em;
2379 height: 2.5em;
2379
2380
2380 .browser-cur-rev, .info_box_elem {
2381 .browser-cur-rev, .info_box_elem {
2381 display: table-cell;
2382 display: table-cell;
2382 vertical-align: middle;
2383 vertical-align: middle;
2383 }
2384 }
2384
2385
2385 .drop-menu {
2386 .drop-menu {
2386 margin: 0 10px;
2387 margin: 0 10px;
2387 }
2388 }
2388
2389
2389 .info_box_elem {
2390 .info_box_elem {
2390 border-top: @border-thickness solid @grey5;
2391 border-top: @border-thickness solid @grey5;
2391 border-bottom: @border-thickness solid @grey5;
2392 border-bottom: @border-thickness solid @grey5;
2392 box-shadow: @button-shadow;
2393 box-shadow: @button-shadow;
2393
2394
2394 #at_rev, a {
2395 #at_rev, a {
2395 padding: 0.6em 0.4em;
2396 padding: 0.6em 0.4em;
2396 margin: 0;
2397 margin: 0;
2397 .box-shadow(none);
2398 .box-shadow(none);
2398 border: 0;
2399 border: 0;
2399 height: 12px;
2400 height: 12px;
2400 color: @grey2;
2401 color: @grey2;
2401 }
2402 }
2402
2403
2403 input#at_rev {
2404 input#at_rev {
2404 max-width: 50px;
2405 max-width: 50px;
2405 text-align: center;
2406 text-align: center;
2406 }
2407 }
2407
2408
2408 &.previous {
2409 &.previous {
2409 border: @border-thickness solid @grey5;
2410 border: @border-thickness solid @grey5;
2410 border-top-left-radius: @border-radius;
2411 border-top-left-radius: @border-radius;
2411 border-bottom-left-radius: @border-radius;
2412 border-bottom-left-radius: @border-radius;
2412
2413
2413 &:hover {
2414 &:hover {
2414 border-color: @grey4;
2415 border-color: @grey4;
2415 }
2416 }
2416
2417
2417 .disabled {
2418 .disabled {
2418 color: @grey5;
2419 color: @grey5;
2419 cursor: not-allowed;
2420 cursor: not-allowed;
2420 opacity: 0.5;
2421 opacity: 0.5;
2421 }
2422 }
2422 }
2423 }
2423
2424
2424 &.next {
2425 &.next {
2425 border: @border-thickness solid @grey5;
2426 border: @border-thickness solid @grey5;
2426 border-top-right-radius: @border-radius;
2427 border-top-right-radius: @border-radius;
2427 border-bottom-right-radius: @border-radius;
2428 border-bottom-right-radius: @border-radius;
2428
2429
2429 &:hover {
2430 &:hover {
2430 border-color: @grey4;
2431 border-color: @grey4;
2431 }
2432 }
2432
2433
2433 .disabled {
2434 .disabled {
2434 color: @grey5;
2435 color: @grey5;
2435 cursor: not-allowed;
2436 cursor: not-allowed;
2436 opacity: 0.5;
2437 opacity: 0.5;
2437 }
2438 }
2438 }
2439 }
2439 }
2440 }
2440
2441
2441 .browser-cur-rev {
2442 .browser-cur-rev {
2442
2443
2443 span{
2444 span{
2444 margin: 0;
2445 margin: 0;
2445 color: @rcblue;
2446 color: @rcblue;
2446 height: 12px;
2447 height: 12px;
2447 display: inline-block;
2448 display: inline-block;
2448 padding: 0.7em 1em ;
2449 padding: 0.7em 1em ;
2449 border: @border-thickness solid @rcblue;
2450 border: @border-thickness solid @rcblue;
2450 margin-right: @padding;
2451 margin-right: @padding;
2451 }
2452 }
2452 }
2453 }
2453
2454
2454 }
2455 }
2455
2456
2456 .select-index-number {
2457 .select-index-number {
2457 margin: 0 0 0 20px;
2458 margin: 0 0 0 20px;
2458 color: @grey3;
2459 color: @grey3;
2459 }
2460 }
2460
2461
2461 .search_activate {
2462 .search_activate {
2462 display: table-cell;
2463 display: table-cell;
2463 vertical-align: middle;
2464 vertical-align: middle;
2464
2465
2465 input, label{
2466 input, label{
2466 margin: 0;
2467 margin: 0;
2467 padding: 0;
2468 padding: 0;
2468 }
2469 }
2469
2470
2470 input{
2471 input{
2471 margin-left: @textmargin;
2472 margin-left: @textmargin;
2472 }
2473 }
2473
2474
2474 }
2475 }
2475 }
2476 }
2476
2477
2477 .browser-cur-rev{
2478 .browser-cur-rev{
2478 margin-bottom: @textmargin;
2479 margin-bottom: @textmargin;
2479 }
2480 }
2480
2481
2481 #node_filter_box_loading{
2482 #node_filter_box_loading{
2482 .info_text;
2483 .info_text;
2483 }
2484 }
2484
2485
2485 .browser-search {
2486 .browser-search {
2486 margin: -25px 0px 5px 0px;
2487 margin: -25px 0px 5px 0px;
2487 }
2488 }
2488
2489
2489 .files-quick-filter {
2490 .files-quick-filter {
2490 float: right;
2491 float: right;
2491 width: 180px;
2492 width: 180px;
2492 position: relative;
2493 position: relative;
2493 }
2494 }
2494
2495
2495 .files-filter-box {
2496 .files-filter-box {
2496 display: flex;
2497 display: flex;
2497 padding: 0px;
2498 padding: 0px;
2498 border-radius: 3px;
2499 border-radius: 3px;
2499 margin-bottom: 0;
2500 margin-bottom: 0;
2500
2501
2501 a {
2502 a {
2502 border: none !important;
2503 border: none !important;
2503 }
2504 }
2504
2505
2505 li {
2506 li {
2506 list-style-type: none
2507 list-style-type: none
2507 }
2508 }
2508 }
2509 }
2509
2510
2510 .files-filter-box-path {
2511 .files-filter-box-path {
2511 line-height: 33px;
2512 line-height: 33px;
2512 padding: 0;
2513 padding: 0;
2513 width: 20px;
2514 width: 20px;
2514 position: absolute;
2515 position: absolute;
2515 z-index: 11;
2516 z-index: 11;
2516 left: 5px;
2517 left: 5px;
2517 }
2518 }
2518
2519
2519 .files-filter-box-input {
2520 .files-filter-box-input {
2520 margin-right: 0;
2521 margin-right: 0;
2521
2522
2522 input {
2523 input {
2523 border: 1px solid @white;
2524 border: 1px solid @white;
2524 padding-left: 25px;
2525 padding-left: 25px;
2525 width: 145px;
2526 width: 145px;
2526
2527
2527 &:hover {
2528 &:hover {
2528 border-color: @grey6;
2529 border-color: @grey6;
2529 }
2530 }
2530
2531
2531 &:focus {
2532 &:focus {
2532 border-color: @grey5;
2533 border-color: @grey5;
2533 }
2534 }
2534 }
2535 }
2535 }
2536 }
2536
2537
2537 .browser-result{
2538 .browser-result{
2538 td a{
2539 td a{
2539 margin-left: 0.5em;
2540 margin-left: 0.5em;
2540 display: inline-block;
2541 display: inline-block;
2541
2542
2542 em {
2543 em {
2543 font-weight: @text-bold-weight;
2544 font-weight: @text-bold-weight;
2544 font-family: @text-bold;
2545 font-family: @text-bold;
2545 }
2546 }
2546 }
2547 }
2547 }
2548 }
2548
2549
2549 .browser-highlight{
2550 .browser-highlight{
2550 background-color: @grey5-alpha;
2551 background-color: @grey5-alpha;
2551 }
2552 }
2552
2553
2553
2554
2554 .edit-file-fieldset #location,
2555 .edit-file-fieldset #location,
2555 .edit-file-fieldset #filename {
2556 .edit-file-fieldset #filename {
2556 display: flex;
2557 display: flex;
2557 width: -moz-available; /* WebKit-based browsers will ignore this. */
2558 width: -moz-available; /* WebKit-based browsers will ignore this. */
2558 width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
2559 width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
2559 width: fill-available;
2560 width: fill-available;
2560 border: 0;
2561 border: 0;
2561 }
2562 }
2562
2563
2563 .path-items {
2564 .path-items {
2564 display: flex;
2565 display: flex;
2565 padding: 0;
2566 padding: 0;
2566 border: 1px solid #eeeeee;
2567 border: 1px solid #eeeeee;
2567 width: 100%;
2568 width: 100%;
2568 float: left;
2569 float: left;
2569
2570
2570 .breadcrumb-path {
2571 .breadcrumb-path {
2571 line-height: 30px;
2572 line-height: 30px;
2572 padding: 0 4px;
2573 padding: 0 4px;
2573 white-space: nowrap;
2574 white-space: nowrap;
2574 }
2575 }
2575
2576
2576 .upload-form {
2577 .upload-form {
2577 margin-top: 46px;
2578 margin-top: 46px;
2578 }
2579 }
2579
2580
2580 .location-path {
2581 .location-path {
2581 width: -moz-available; /* WebKit-based browsers will ignore this. */
2582 width: -moz-available; /* WebKit-based browsers will ignore this. */
2582 width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
2583 width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
2583 width: fill-available;
2584 width: fill-available;
2584
2585
2585 .file-name-input {
2586 .file-name-input {
2586 padding: 0.5em 0;
2587 padding: 0.5em 0;
2587 }
2588 }
2588
2589
2589 }
2590 }
2590
2591
2591 ul {
2592 ul {
2592 display: flex;
2593 display: flex;
2593 margin: 0;
2594 margin: 0;
2594 padding: 0;
2595 padding: 0;
2595 width: 100%;
2596 width: 100%;
2596 }
2597 }
2597
2598
2598 li {
2599 li {
2599 list-style-type: none;
2600 list-style-type: none;
2600 }
2601 }
2601
2602
2602 }
2603 }
2603
2604
2604 .editor-items {
2605 .editor-items {
2605 height: 40px;
2606 height: 40px;
2606 margin: 10px 0 -17px 10px;
2607 margin: 10px 0 -17px 10px;
2607
2608
2608 .editor-action {
2609 .editor-action {
2609 cursor: pointer;
2610 cursor: pointer;
2610 }
2611 }
2611
2612
2612 .editor-action.active {
2613 .editor-action.active {
2613 border-bottom: 2px solid #5C5C5C;
2614 border-bottom: 2px solid #5C5C5C;
2614 }
2615 }
2615
2616
2616 li {
2617 li {
2617 list-style-type: none;
2618 list-style-type: none;
2618 }
2619 }
2619 }
2620 }
2620
2621
2621 .edit-file-fieldset .message textarea {
2622 .edit-file-fieldset .message textarea {
2622 border: 1px solid #eeeeee;
2623 border: 1px solid #eeeeee;
2623 }
2624 }
2624
2625
2625 #files_data .codeblock {
2626 #files_data .codeblock {
2626 background-color: #F5F5F5;
2627 background-color: #F5F5F5;
2627 }
2628 }
2628
2629
2629 #editor_preview {
2630 #editor_preview {
2630 background: white;
2631 background: white;
2631 }
2632 }
2632
2633
2633 .show-editor {
2634 .show-editor {
2634 padding: 10px;
2635 padding: 10px;
2635 background-color: white;
2636 background-color: white;
2636
2637
2637 }
2638 }
2638
2639
2639 .show-preview {
2640 .show-preview {
2640 padding: 10px;
2641 padding: 10px;
2641 background-color: white;
2642 background-color: white;
2642 border-left: 1px solid #eeeeee;
2643 border-left: 1px solid #eeeeee;
2643 }
2644 }
2644 // quick filter
2645 // quick filter
2645 .grid-quick-filter {
2646 .grid-quick-filter {
2646 float: right;
2647 float: right;
2647 position: relative;
2648 position: relative;
2648 }
2649 }
2649
2650
2650 .grid-filter-box {
2651 .grid-filter-box {
2651 display: flex;
2652 display: flex;
2652 padding: 0px;
2653 padding: 0px;
2653 border-radius: 3px;
2654 border-radius: 3px;
2654 margin-bottom: 0;
2655 margin-bottom: 0;
2655
2656
2656 a {
2657 a {
2657 border: none !important;
2658 border: none !important;
2658 }
2659 }
2659
2660
2660 li {
2661 li {
2661 list-style-type: none
2662 list-style-type: none
2662 }
2663 }
2663
2664
2664 }
2665 }
2665
2666
2666 .grid-filter-box-icon {
2667 .grid-filter-box-icon {
2667 line-height: 33px;
2668 line-height: 33px;
2668 padding: 0;
2669 padding: 0;
2669 width: 20px;
2670 width: 20px;
2670 position: absolute;
2671 position: absolute;
2671 z-index: 11;
2672 z-index: 11;
2672 left: 5px;
2673 left: 5px;
2673 }
2674 }
2674
2675
2675 .grid-filter-box-input {
2676 .grid-filter-box-input {
2676 margin-right: 0;
2677 margin-right: 0;
2677
2678
2678 input {
2679 input {
2679 border: 1px solid @white;
2680 border: 1px solid @white;
2680 padding-left: 25px;
2681 padding-left: 25px;
2681 width: 145px;
2682 width: 145px;
2682
2683
2683 &:hover {
2684 &:hover {
2684 border-color: @grey6;
2685 border-color: @grey6;
2685 }
2686 }
2686
2687
2687 &:focus {
2688 &:focus {
2688 border-color: @grey5;
2689 border-color: @grey5;
2689 }
2690 }
2690 }
2691 }
2691 }
2692 }
2692
2693
2693
2694
2694
2695
2695 // Search
2696 // Search
2696
2697
2697 .search-form{
2698 .search-form{
2698 #q {
2699 #q {
2699 width: @search-form-width;
2700 width: @search-form-width;
2700 }
2701 }
2701 .fields{
2702 .fields{
2702 margin: 0 0 @space;
2703 margin: 0 0 @space;
2703 }
2704 }
2704
2705
2705 label{
2706 label{
2706 display: inline-block;
2707 display: inline-block;
2707 margin-right: @textmargin;
2708 margin-right: @textmargin;
2708 padding-top: 0.25em;
2709 padding-top: 0.25em;
2709 }
2710 }
2710
2711
2711
2712
2712 .results{
2713 .results{
2713 clear: both;
2714 clear: both;
2714 margin: 0 0 @padding;
2715 margin: 0 0 @padding;
2715 }
2716 }
2716
2717
2717 .search-tags {
2718 .search-tags {
2718 padding: 5px 0;
2719 padding: 5px 0;
2719 }
2720 }
2720 }
2721 }
2721
2722
2722 div.search-feedback-items {
2723 div.search-feedback-items {
2723 display: inline-block;
2724 display: inline-block;
2724 }
2725 }
2725
2726
2726 div.search-code-body {
2727 div.search-code-body {
2727 background-color: #ffffff; padding: 5px 0 5px 10px;
2728 background-color: #ffffff; padding: 5px 0 5px 10px;
2728 pre {
2729 pre {
2729 .match { background-color: #faffa6;}
2730 .match { background-color: #faffa6;}
2730 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
2731 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
2731 }
2732 }
2732 }
2733 }
2733
2734
2734 .expand_commit.search {
2735 .expand_commit.search {
2735 .show_more.open {
2736 .show_more.open {
2736 height: auto;
2737 height: auto;
2737 max-height: none;
2738 max-height: none;
2738 }
2739 }
2739 }
2740 }
2740
2741
2741 .search-results {
2742 .search-results {
2742
2743
2743 h2 {
2744 h2 {
2744 margin-bottom: 0;
2745 margin-bottom: 0;
2745 }
2746 }
2746 .codeblock {
2747 .codeblock {
2747 border: none;
2748 border: none;
2748 background: transparent;
2749 background: transparent;
2749 }
2750 }
2750
2751
2751 .codeblock-header {
2752 .codeblock-header {
2752 border: none;
2753 border: none;
2753 background: transparent;
2754 background: transparent;
2754 }
2755 }
2755
2756
2756 .code-body {
2757 .code-body {
2757 border: @border-thickness solid @grey6;
2758 border: @border-thickness solid @grey6;
2758 .border-radius(@border-radius);
2759 .border-radius(@border-radius);
2759 }
2760 }
2760
2761
2761 .td-commit {
2762 .td-commit {
2762 &:extend(pre);
2763 &:extend(pre);
2763 border-bottom: @border-thickness solid @border-default-color;
2764 border-bottom: @border-thickness solid @border-default-color;
2764 }
2765 }
2765
2766
2766 .message {
2767 .message {
2767 height: auto;
2768 height: auto;
2768 max-width: 350px;
2769 max-width: 350px;
2769 white-space: normal;
2770 white-space: normal;
2770 text-overflow: initial;
2771 text-overflow: initial;
2771 overflow: visible;
2772 overflow: visible;
2772
2773
2773 .match { background-color: #faffa6;}
2774 .match { background-color: #faffa6;}
2774 .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; }
2775 .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; }
2775 }
2776 }
2776
2777
2777 .path {
2778 .path {
2778 border-bottom: none !important;
2779 border-bottom: none !important;
2779 border-left: 1px solid @grey6 !important;
2780 border-left: 1px solid @grey6 !important;
2780 border-right: 1px solid @grey6 !important;
2781 border-right: 1px solid @grey6 !important;
2781 }
2782 }
2782 }
2783 }
2783
2784
2784 table.rctable td.td-search-results div {
2785 table.rctable td.td-search-results div {
2785 max-width: 100%;
2786 max-width: 100%;
2786 }
2787 }
2787
2788
2788 #tip-box, .tip-box{
2789 #tip-box, .tip-box{
2789 padding: @menupadding/2;
2790 padding: @menupadding/2;
2790 display: block;
2791 display: block;
2791 border: @border-thickness solid @border-highlight-color;
2792 border: @border-thickness solid @border-highlight-color;
2792 .border-radius(@border-radius);
2793 .border-radius(@border-radius);
2793 background-color: white;
2794 background-color: white;
2794 z-index: 99;
2795 z-index: 99;
2795 white-space: pre-wrap;
2796 white-space: pre-wrap;
2796 }
2797 }
2797
2798
2798 #linktt {
2799 #linktt {
2799 width: 79px;
2800 width: 79px;
2800 }
2801 }
2801
2802
2802 #help_kb .modal-content{
2803 #help_kb .modal-content{
2803 max-width: 800px;
2804 max-width: 800px;
2804 margin: 10% auto;
2805 margin: 10% auto;
2805
2806
2806 table{
2807 table{
2807 td,th{
2808 td,th{
2808 border-bottom: none;
2809 border-bottom: none;
2809 line-height: 2.5em;
2810 line-height: 2.5em;
2810 }
2811 }
2811 th{
2812 th{
2812 padding-bottom: @textmargin/2;
2813 padding-bottom: @textmargin/2;
2813 }
2814 }
2814 td.keys{
2815 td.keys{
2815 text-align: center;
2816 text-align: center;
2816 }
2817 }
2817 }
2818 }
2818
2819
2819 .block-left{
2820 .block-left{
2820 width: 45%;
2821 width: 45%;
2821 margin-right: 5%;
2822 margin-right: 5%;
2822 }
2823 }
2823 .modal-footer{
2824 .modal-footer{
2824 clear: both;
2825 clear: both;
2825 }
2826 }
2826 .key.tag{
2827 .key.tag{
2827 padding: 0.5em;
2828 padding: 0.5em;
2828 background-color: @rcblue;
2829 background-color: @rcblue;
2829 color: white;
2830 color: white;
2830 border-color: @rcblue;
2831 border-color: @rcblue;
2831 .box-shadow(none);
2832 .box-shadow(none);
2832 }
2833 }
2833 }
2834 }
2834
2835
2835
2836
2836
2837
2837 //--- IMPORTS FOR REFACTORED STYLES ------------------//
2838 //--- IMPORTS FOR REFACTORED STYLES ------------------//
2838
2839
2839 @import 'statistics-graph';
2840 @import 'statistics-graph';
2840 @import 'tables';
2841 @import 'tables';
2841 @import 'forms';
2842 @import 'forms';
2842 @import 'diff';
2843 @import 'diff';
2843 @import 'summary';
2844 @import 'summary';
2844 @import 'navigation';
2845 @import 'navigation';
2845
2846
2846 //--- SHOW/HIDE SECTIONS --//
2847 //--- SHOW/HIDE SECTIONS --//
2847
2848
2848 .btn-collapse {
2849 .btn-collapse {
2849 float: right;
2850 float: right;
2850 text-align: right;
2851 text-align: right;
2851 font-family: @text-light;
2852 font-family: @text-light;
2852 font-size: @basefontsize;
2853 font-size: @basefontsize;
2853 cursor: pointer;
2854 cursor: pointer;
2854 border: none;
2855 border: none;
2855 color: @rcblue;
2856 color: @rcblue;
2856 }
2857 }
2857
2858
2858 table.rctable,
2859 table.rctable,
2859 table.dataTable {
2860 table.dataTable {
2860 .btn-collapse {
2861 .btn-collapse {
2861 float: right;
2862 float: right;
2862 text-align: right;
2863 text-align: right;
2863 }
2864 }
2864 }
2865 }
2865
2866
2866 table.rctable {
2867 table.rctable {
2867 &.permissions {
2868 &.permissions {
2868
2869
2869 th.td-owner {
2870 th.td-owner {
2870 padding: 0;
2871 padding: 0;
2871 }
2872 }
2872
2873
2873 th {
2874 th {
2874 font-weight: normal;
2875 font-weight: normal;
2875 padding: 0 5px;
2876 padding: 0 5px;
2876 }
2877 }
2877
2878
2878 }
2879 }
2879 }
2880 }
2880
2881
2881
2882
2882 // TODO: johbo: Fix for IE10, this avoids that we see a border
2883 // TODO: johbo: Fix for IE10, this avoids that we see a border
2883 // and padding around checkboxes and radio boxes. Move to the right place,
2884 // and padding around checkboxes and radio boxes. Move to the right place,
2884 // or better: Remove this once we did the form refactoring.
2885 // or better: Remove this once we did the form refactoring.
2885 input[type=checkbox],
2886 input[type=checkbox],
2886 input[type=radio] {
2887 input[type=radio] {
2887 padding: 0;
2888 padding: 0;
2888 border: none;
2889 border: none;
2889 }
2890 }
2890
2891
2891 .toggle-ajax-spinner{
2892 .toggle-ajax-spinner{
2892 height: 16px;
2893 height: 16px;
2893 width: 16px;
2894 width: 16px;
2894 }
2895 }
2895
2896
2896
2897
2897 .markup-form .clearfix {
2898 .markup-form .clearfix {
2898 .border-radius(@border-radius);
2899 .border-radius(@border-radius);
2899 margin: 0px;
2900 margin: 0px;
2900 }
2901 }
2901
2902
2902 .markup-form-area {
2903 .markup-form-area {
2903 padding: 8px 12px;
2904 padding: 8px 12px;
2904 border: 1px solid @grey4;
2905 border: 1px solid @grey4;
2905 .border-radius(@border-radius);
2906 .border-radius(@border-radius);
2906 }
2907 }
2907
2908
2908 .markup-form-area-header .nav-links {
2909 .markup-form-area-header .nav-links {
2909 display: flex;
2910 display: flex;
2910 flex-flow: row wrap;
2911 flex-flow: row wrap;
2911 -webkit-flex-flow: row wrap;
2912 -webkit-flex-flow: row wrap;
2912 width: 100%;
2913 width: 100%;
2913 }
2914 }
2914
2915
2915 .markup-form-area-footer {
2916 .markup-form-area-footer {
2916 display: flex;
2917 display: flex;
2917 }
2918 }
2918
2919
2919 .markup-form-area-footer .toolbar {
2920 .markup-form-area-footer .toolbar {
2920
2921
2921 }
2922 }
2922
2923
2923 // markup Form
2924 // markup Form
2924 div.markup-form {
2925 div.markup-form {
2925 margin-top: 20px;
2926 margin-top: 20px;
2926 }
2927 }
2927
2928
2928 .markup-form strong {
2929 .markup-form strong {
2929 display: block;
2930 display: block;
2930 margin-bottom: 15px;
2931 margin-bottom: 15px;
2931 }
2932 }
2932
2933
2933 .markup-form textarea {
2934 .markup-form textarea {
2934 width: 100%;
2935 width: 100%;
2935 height: 100px;
2936 height: 100px;
2936 font-family: @text-monospace;
2937 font-family: @text-monospace;
2937 }
2938 }
2938
2939
2939 form.markup-form {
2940 form.markup-form {
2940 margin-top: 10px;
2941 margin-top: 10px;
2941 margin-left: 10px;
2942 margin-left: 10px;
2942 }
2943 }
2943
2944
2944 .markup-form .comment-block-ta,
2945 .markup-form .comment-block-ta,
2945 .markup-form .preview-box {
2946 .markup-form .preview-box {
2946 .border-radius(@border-radius);
2947 .border-radius(@border-radius);
2947 .box-sizing(border-box);
2948 .box-sizing(border-box);
2948 background-color: white;
2949 background-color: white;
2949 }
2950 }
2950
2951
2951 .markup-form .preview-box.unloaded {
2952 .markup-form .preview-box.unloaded {
2952 height: 50px;
2953 height: 50px;
2953 text-align: center;
2954 text-align: center;
2954 padding: 20px;
2955 padding: 20px;
2955 background-color: white;
2956 background-color: white;
2956 }
2957 }
2957
2958
2958
2959
2959 .dropzone-wrapper {
2960 .dropzone-wrapper {
2960 border: 1px solid @grey5;
2961 border: 1px solid @grey5;
2961 padding: 20px;
2962 padding: 20px;
2962 }
2963 }
2963
2964
2964 .dropzone,
2965 .dropzone,
2965 .dropzone-pure {
2966 .dropzone-pure {
2966 border: 2px dashed @grey5;
2967 border: 2px dashed @grey5;
2967 border-radius: 5px;
2968 border-radius: 5px;
2968 background: white;
2969 background: white;
2969 min-height: 200px;
2970 min-height: 200px;
2970 padding: 54px;
2971 padding: 54px;
2971
2972
2972 .dz-message {
2973 .dz-message {
2973 font-weight: 700;
2974 font-weight: 700;
2974 text-align: center;
2975 text-align: center;
2975 margin: 2em 0;
2976 margin: 2em 0;
2976 }
2977 }
2977
2978
2978 }
2979 }
2979
2980
2980 .dz-preview {
2981 .dz-preview {
2981 margin: 10px 0 !important;
2982 margin: 10px 0 !important;
2982 position: relative;
2983 position: relative;
2983 vertical-align: top;
2984 vertical-align: top;
2984 padding: 10px;
2985 padding: 10px;
2985 border-bottom: 1px solid @grey5;
2986 border-bottom: 1px solid @grey5;
2986 }
2987 }
2987
2988
2988 .dz-filename {
2989 .dz-filename {
2989 font-weight: 700;
2990 font-weight: 700;
2990 float: left;
2991 float: left;
2991 }
2992 }
2992
2993
2993 .dz-sending {
2994 .dz-sending {
2994 float: right;
2995 float: right;
2995 }
2996 }
2996
2997
2997 .dz-response {
2998 .dz-response {
2998 clear: both
2999 clear: both
2999 }
3000 }
3000
3001
3001 .dz-filename-size {
3002 .dz-filename-size {
3002 float: right
3003 float: right
3003 }
3004 }
3004
3005
3005 .dz-error-message {
3006 .dz-error-message {
3006 color: @alert2;
3007 color: @alert2;
3007 padding-top: 10px;
3008 padding-top: 10px;
3008 clear: both;
3009 clear: both;
3009 }
3010 }
3010
3011
3011
3012
3012 .user-hovercard {
3013 .user-hovercard {
3013 padding: 5px;
3014 padding: 5px;
3014 }
3015 }
3015
3016
3016 .user-hovercard-icon {
3017 .user-hovercard-icon {
3017 display: inline;
3018 display: inline;
3018 padding: 0;
3019 padding: 0;
3019 box-sizing: content-box;
3020 box-sizing: content-box;
3020 border-radius: 50%;
3021 border-radius: 50%;
3021 float: left;
3022 float: left;
3022 }
3023 }
3023
3024
3024 .user-hovercard-name {
3025 .user-hovercard-name {
3025 float: right;
3026 float: right;
3026 vertical-align: top;
3027 vertical-align: top;
3027 padding-left: 10px;
3028 padding-left: 10px;
3028 min-width: 150px;
3029 min-width: 150px;
3029 }
3030 }
3030
3031
3031 .user-hovercard-bio {
3032 .user-hovercard-bio {
3032 clear: both;
3033 clear: both;
3033 padding-top: 10px;
3034 padding-top: 10px;
3034 }
3035 }
3035
3036
3036 .user-hovercard-header {
3037 .user-hovercard-header {
3037 clear: both;
3038 clear: both;
3038 min-height: 10px;
3039 min-height: 10px;
3039 }
3040 }
3040
3041
3041 .user-hovercard-footer {
3042 .user-hovercard-footer {
3042 clear: both;
3043 clear: both;
3043 min-height: 10px;
3044 min-height: 10px;
3044 }
3045 }
3045
3046
3046 .user-group-hovercard {
3047 .user-group-hovercard {
3047 padding: 5px;
3048 padding: 5px;
3048 }
3049 }
3049
3050
3050 .user-group-hovercard-icon {
3051 .user-group-hovercard-icon {
3051 display: inline;
3052 display: inline;
3052 padding: 0;
3053 padding: 0;
3053 box-sizing: content-box;
3054 box-sizing: content-box;
3054 border-radius: 50%;
3055 border-radius: 50%;
3055 float: left;
3056 float: left;
3056 }
3057 }
3057
3058
3058 .user-group-hovercard-name {
3059 .user-group-hovercard-name {
3059 float: left;
3060 float: left;
3060 vertical-align: top;
3061 vertical-align: top;
3061 padding-left: 10px;
3062 padding-left: 10px;
3062 min-width: 150px;
3063 min-width: 150px;
3063 }
3064 }
3064
3065
3065 .user-group-hovercard-icon i {
3066 .user-group-hovercard-icon i {
3066 border: 1px solid @grey4;
3067 border: 1px solid @grey4;
3067 border-radius: 4px;
3068 border-radius: 4px;
3068 }
3069 }
3069
3070
3070 .user-group-hovercard-bio {
3071 .user-group-hovercard-bio {
3071 clear: both;
3072 clear: both;
3072 padding-top: 10px;
3073 padding-top: 10px;
3073 line-height: 1.0em;
3074 line-height: 1.0em;
3074 }
3075 }
3075
3076
3076 .user-group-hovercard-header {
3077 .user-group-hovercard-header {
3077 clear: both;
3078 clear: both;
3078 min-height: 10px;
3079 min-height: 10px;
3079 }
3080 }
3080
3081
3081 .user-group-hovercard-footer {
3082 .user-group-hovercard-footer {
3082 clear: both;
3083 clear: both;
3083 min-height: 10px;
3084 min-height: 10px;
3084 }
3085 }
3085
3086
3086 .pr-hovercard-header {
3087 .pr-hovercard-header {
3087 clear: both;
3088 clear: both;
3088 display: block;
3089 display: block;
3089 line-height: 20px;
3090 line-height: 20px;
3090 }
3091 }
3091
3092
3092 .pr-hovercard-user {
3093 .pr-hovercard-user {
3093 display: flex;
3094 display: flex;
3094 align-items: center;
3095 align-items: center;
3095 padding-left: 5px;
3096 padding-left: 5px;
3096 }
3097 }
3097
3098
3098 .pr-hovercard-title {
3099 .pr-hovercard-title {
3099 padding-top: 5px;
3100 padding-top: 5px;
3100 }
3101 }
3101
3102
3102 .action-divider {
3103 .action-divider {
3103 opacity: 0.5;
3104 opacity: 0.5;
3104 }
3105 }
3105
3106
3106 .details-inline-block {
3107 .details-inline-block {
3107 display: inline-block;
3108 display: inline-block;
3108 position: relative;
3109 position: relative;
3109 }
3110 }
3110
3111
3111 .details-inline-block summary {
3112 .details-inline-block summary {
3112 list-style: none;
3113 list-style: none;
3113 }
3114 }
3114
3115
3115 details:not([open]) > :not(summary) {
3116 details:not([open]) > :not(summary) {
3116 display: none !important;
3117 display: none !important;
3117 }
3118 }
3118
3119
3119 .details-reset > summary {
3120 .details-reset > summary {
3120 list-style: none;
3121 list-style: none;
3121 }
3122 }
3122
3123
3123 .details-reset > summary::-webkit-details-marker {
3124 .details-reset > summary::-webkit-details-marker {
3124 display: none;
3125 display: none;
3125 }
3126 }
3126
3127
3127 .details-dropdown {
3128 .details-dropdown {
3128 position: absolute;
3129 position: absolute;
3129 top: 100%;
3130 top: 100%;
3130 width: 185px;
3131 width: 185px;
3131 list-style: none;
3132 list-style: none;
3132 background-color: #fff;
3133 background-color: #fff;
3133 background-clip: padding-box;
3134 background-clip: padding-box;
3134 border: 1px solid @grey5;
3135 border: 1px solid @grey5;
3135 box-shadow: 0 8px 24px rgba(149, 157, 165, .2);
3136 box-shadow: 0 8px 24px rgba(149, 157, 165, .2);
3136 left: -150px;
3137 left: -150px;
3137 text-align: left;
3138 text-align: left;
3138 z-index: 90;
3139 z-index: 90;
3139 }
3140 }
3140
3141
3141 .dropdown-divider {
3142 .dropdown-divider {
3142 display: block;
3143 display: block;
3143 height: 0;
3144 height: 0;
3144 margin: 8px 0;
3145 margin: 8px 0;
3145 border-top: 1px solid @grey5;
3146 border-top: 1px solid @grey5;
3146 }
3147 }
3147
3148
3148 .dropdown-item {
3149 .dropdown-item {
3149 display: block;
3150 display: block;
3150 padding: 4px 8px 4px 16px;
3151 padding: 4px 8px 4px 16px;
3151 overflow: hidden;
3152 overflow: hidden;
3152 text-overflow: ellipsis;
3153 text-overflow: ellipsis;
3153 white-space: nowrap;
3154 white-space: nowrap;
3154 font-weight: normal;
3155 font-weight: normal;
3155 }
3156 }
3156
3157
3157 .right-sidebar {
3158 .right-sidebar {
3158 position: fixed;
3159 position: fixed;
3159 top: 0px;
3160 top: 0px;
3160 bottom: 0;
3161 bottom: 0;
3161 right: 0;
3162 right: 0;
3162
3163
3163 background: #fafafa;
3164 background: #fafafa;
3164 z-index: 50;
3165 z-index: 50;
3165 }
3166 }
3166
3167
3167 .right-sidebar {
3168 .right-sidebar {
3168 border-left: 1px solid @grey5;
3169 border-left: 1px solid @grey5;
3169 }
3170 }
3170
3171
3171 .right-sidebar.right-sidebar-expanded {
3172 .right-sidebar.right-sidebar-expanded {
3172 width: 300px;
3173 width: 300px;
3173 overflow: scroll;
3174 overflow: scroll;
3174 }
3175 }
3175
3176
3176 .right-sidebar.right-sidebar-collapsed {
3177 .right-sidebar.right-sidebar-collapsed {
3177 width: 40px;
3178 width: 40px;
3178 padding: 0;
3179 padding: 0;
3179 display: block;
3180 display: block;
3180 overflow: hidden;
3181 overflow: hidden;
3181 }
3182 }
3182
3183
3183 .sidenav {
3184 .sidenav {
3184 float: right;
3185 float: right;
3185 will-change: min-height;
3186 will-change: min-height;
3186 background: #fafafa;
3187 background: #fafafa;
3187 width: 100%;
3188 width: 100%;
3188 }
3189 }
3189
3190
3190 .sidebar-toggle {
3191 .sidebar-toggle {
3191 height: 30px;
3192 height: 30px;
3192 text-align: center;
3193 text-align: center;
3193 margin: 15px 0px 0 0;
3194 margin: 15px 0px 0 0;
3194 }
3195 }
3195
3196
3196 .sidebar-toggle a {
3197 .sidebar-toggle a {
3197
3198
3198 }
3199 }
3199
3200
3200 .sidebar-content {
3201 .sidebar-content {
3201 margin-left: 15px;
3202 margin-left: 15px;
3202 margin-right: 15px;
3203 margin-right: 15px;
3203 }
3204 }
3204
3205
3205 .sidebar-heading {
3206 .sidebar-heading {
3206 font-size: 1.2em;
3207 font-size: 1.2em;
3207 font-weight: 700;
3208 font-weight: 700;
3208 margin-top: 10px;
3209 margin-top: 10px;
3209 }
3210 }
3210
3211
3211 .sidebar-element {
3212 .sidebar-element {
3212 margin-top: 20px;
3213 margin-top: 20px;
3213 }
3214 }
3214
3215
3215 .right-sidebar-collapsed-state {
3216 .right-sidebar-collapsed-state {
3216 display: flex;
3217 display: flex;
3217 flex-direction: column;
3218 flex-direction: column;
3218 justify-content: center;
3219 justify-content: center;
3219 align-items: center;
3220 align-items: center;
3220 padding: 0 10px;
3221 padding: 0 10px;
3221 cursor: pointer;
3222 cursor: pointer;
3222 font-size: 1.3em;
3223 font-size: 1.3em;
3223 margin: 0 -15px;
3224 margin: 0 -15px;
3224 }
3225 }
3225
3226
3226 .right-sidebar-collapsed-state:hover {
3227 .right-sidebar-collapsed-state:hover {
3227 background-color: @grey5;
3228 background-color: @grey5;
3228 }
3229 }
3229
3230
3230 .old-comments-marker {
3231 .old-comments-marker {
3231 text-align: left;
3232 text-align: left;
3232 }
3233 }
3233
3234
3234 .old-comments-marker td {
3235 .old-comments-marker td {
3235 padding-top: 15px;
3236 padding-top: 15px;
3236 border-bottom: 1px solid @grey5;
3237 border-bottom: 1px solid @grey5;
3237 }
3238 }
@@ -1,144 +1,144 b''
1 <%namespace name="base" file="/base/base.mako"/>
1 <%namespace name="base" file="/base/base.mako"/>
2
2
3 <div class="panel panel-default">
3 <div class="panel panel-default">
4 <div class="panel-body">
4 <div class="panel-body">
5 <div style="height: 35px">
5 <div style="height: 35px">
6 <%
6 <%
7 selected_filter = 'all'
7 selected_filter = 'all'
8 if c.closed:
8 if c.closed:
9 selected_filter = 'all_closed'
9 selected_filter = 'all_closed'
10 %>
10 %>
11
11
12 <ul class="button-links">
12 <ul class="button-links">
13 <li class="btn ${h.is_active('all', selected_filter)}"><a href="${h.route_path('my_account_pullrequests')}">${_('All')}</a></li>
13 <li class="btn ${h.is_active('all', selected_filter)}"><a href="${h.route_path('my_account_pullrequests')}">${_('All')}</a></li>
14 <li class="btn ${h.is_active('all_closed', selected_filter)}"><a href="${h.route_path('my_account_pullrequests', _query={'pr_show_closed':1})}">${_('All + Closed')}</a></li>
14 <li class="btn ${h.is_active('all_closed', selected_filter)}"><a href="${h.route_path('my_account_pullrequests', _query={'pr_show_closed':1})}">${_('All + Closed')}</a></li>
15 </ul>
15 </ul>
16
16
17 <div class="grid-quick-filter">
17 <div class="grid-quick-filter">
18 <ul class="grid-filter-box">
18 <ul class="grid-filter-box">
19 <li class="grid-filter-box-icon">
19 <li class="grid-filter-box-icon">
20 <i class="icon-search"></i>
20 <i class="icon-search"></i>
21 </li>
21 </li>
22 <li class="grid-filter-box-input">
22 <li class="grid-filter-box-input">
23 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
23 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
24 </li>
24 </li>
25 </ul>
25 </ul>
26 </div>
26 </div>
27 </div>
27 </div>
28 </div>
28 </div>
29 </div>
29 </div>
30
30
31 <div class="panel panel-default">
31 <div class="panel panel-default">
32 <div class="panel-heading">
32 <div class="panel-heading">
33 <h3 class="panel-title">${_('Pull Requests You Participate In')}</h3>
33 <h3 class="panel-title">${_('Pull Requests You Participate In')}</h3>
34 </div>
34 </div>
35 <div class="panel-body panel-body-min-height">
35 <div class="panel-body panel-body-min-height">
36 <table id="pull_request_list_table" class="rctable table-bordered"></table>
36 <table id="pull_request_list_table" class="rctable table-bordered"></table>
37 </div>
37 </div>
38 </div>
38 </div>
39
39
40 <script type="text/javascript">
40 <script type="text/javascript">
41 $(document).ready(function () {
41 $(document).ready(function () {
42
42
43 var $pullRequestListTable = $('#pull_request_list_table');
43 var $pullRequestListTable = $('#pull_request_list_table');
44
44
45 // participating object list
45 // participating object list
46 $pullRequestListTable.DataTable({
46 $pullRequestListTable.DataTable({
47 processing: true,
47 processing: true,
48 serverSide: true,
48 serverSide: true,
49 ajax: {
49 ajax: {
50 "url": "${h.route_path('my_account_pullrequests_data')}",
50 "url": "${h.route_path('my_account_pullrequests_data')}",
51 "data": function (d) {
51 "data": function (d) {
52 d.closed = "${c.closed}";
52 d.closed = "${c.closed}";
53 },
53 },
54 "dataSrc": function (json) {
54 "dataSrc": function (json) {
55 return json.data;
55 return json.data;
56 }
56 }
57 },
57 },
58
58
59 dom: 'rtp',
59 dom: 'rtp',
60 pageLength: ${c.visual.dashboard_items},
60 pageLength: ${c.visual.dashboard_items},
61 order: [[2, "desc"]],
61 order: [[1, "desc"]],
62 columns: [
62 columns: [
63 {
63 {
64 data: {
64 data: {
65 "_": "target_repo",
66 "sort": "target_repo"
67 }, title: "${_('Target Repo')}", className: "td-targetrepo", orderable: false
68 },
69 {
70 data: {
71 "_": "status",
65 "_": "status",
72 "sort": "status"
66 "sort": "status"
73 }, title: "", className: "td-status", orderable: false
67 }, title: "", className: "td-status", orderable: false
74 },
68 },
75 {
69 {
76 data: {
70 data: {
77 "_": "name",
71 "_": "name",
78 "sort": "name_raw"
72 "sort": "name_raw"
79 }, title: "${_('Id')}", className: "td-componentname", "type": "num"
73 }, title: "${_('Id')}", className: "td-componentname", "type": "num"
80 },
74 },
81 {
75 {
82 data: {
76 data: {
83 "_": "title",
77 "_": "title",
84 "sort": "title"
78 "sort": "title"
85 }, title: "${_('Title')}", className: "td-description"
79 }, title: "${_('Title')}", className: "td-description"
86 },
80 },
87 {
81 {
88 data: {
82 data: {
89 "_": "author",
83 "_": "author",
90 "sort": "author_raw"
84 "sort": "author_raw"
91 }, title: "${_('Author')}", className: "td-user", orderable: false
85 }, title: "${_('Author')}", className: "td-user", orderable: false
92 },
86 },
93 {
87 {
94 data: {
88 data: {
95 "_": "comments",
89 "_": "comments",
96 "sort": "comments_raw"
90 "sort": "comments_raw"
97 }, title: "", className: "td-comments", orderable: false
91 }, title: "", className: "td-comments", orderable: false
98 },
92 },
99 {
93 {
100 data: {
94 data: {
101 "_": "updated_on",
95 "_": "updated_on",
102 "sort": "updated_on_raw"
96 "sort": "updated_on_raw"
103 }, title: "${_('Last Update')}", className: "td-time"
97 }, title: "${_('Last Update')}", className: "td-time"
104 }
98 },
99 {
100 data: {
101 "_": "target_repo",
102 "sort": "target_repo"
103 }, title: "${_('Target Repo')}", className: "td-targetrepo", orderable: false
104 },
105 ],
105 ],
106 language: {
106 language: {
107 paginate: DEFAULT_GRID_PAGINATION,
107 paginate: DEFAULT_GRID_PAGINATION,
108 sProcessing: _gettext('loading...'),
108 sProcessing: _gettext('loading...'),
109 emptyTable: _gettext("There are currently no open pull requests requiring your participation.")
109 emptyTable: _gettext("There are currently no open pull requests requiring your participation.")
110 },
110 },
111 "drawCallback": function (settings, json) {
111 "drawCallback": function (settings, json) {
112 timeagoActivate();
112 timeagoActivate();
113 tooltipActivate();
113 tooltipActivate();
114 },
114 },
115 "createdRow": function (row, data, index) {
115 "createdRow": function (row, data, index) {
116 if (data['closed']) {
116 if (data['closed']) {
117 $(row).addClass('closed');
117 $(row).addClass('closed');
118 }
118 }
119 if (data['owned']) {
119 if (data['owned']) {
120 $(row).addClass('owned');
120 $(row).addClass('owned');
121 }
121 }
122 }
122 }
123 });
123 });
124 $pullRequestListTable.on('xhr.dt', function (e, settings, json, xhr) {
124 $pullRequestListTable.on('xhr.dt', function (e, settings, json, xhr) {
125 $pullRequestListTable.css('opacity', 1);
125 $pullRequestListTable.css('opacity', 1);
126 });
126 });
127
127
128 $pullRequestListTable.on('preXhr.dt', function (e, settings, data) {
128 $pullRequestListTable.on('preXhr.dt', function (e, settings, data) {
129 $pullRequestListTable.css('opacity', 0.3);
129 $pullRequestListTable.css('opacity', 0.3);
130 });
130 });
131
131
132 // filter
132 // filter
133 $('#q_filter').on('keyup',
133 $('#q_filter').on('keyup',
134 $.debounce(250, function () {
134 $.debounce(250, function () {
135 $pullRequestListTable.DataTable().search(
135 $pullRequestListTable.DataTable().search(
136 $('#q_filter').val()
136 $('#q_filter').val()
137 ).draw();
137 ).draw();
138 })
138 })
139 );
139 );
140
140
141 });
141 });
142
142
143
143
144 </script>
144 </script>
@@ -1,489 +1,489 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 <%def name="render_description(description, stylify_metatags)">
40 <%def name="render_description(description, stylify_metatags)">
41 <%
41 <%
42 tags = []
42 tags = []
43 if stylify_metatags:
43 if stylify_metatags:
44 tags, description = h.extract_metatags(description)
44 tags, description = h.extract_metatags(description)
45 %>
45 %>
46 % for tag_type, tag in tags:
46 % for tag_type, tag in tags:
47 ${h.style_metatag(tag_type, tag)|n,trim}
47 ${h.style_metatag(tag_type, tag)|n,trim}
48 % endfor
48 % endfor
49 <code style="white-space: pre-wrap">${description}</code>
49 <code style="white-space: pre-wrap">${description}</code>
50 </%def>
50 </%def>
51
51
52 ## REPOSITORY RENDERERS
52 ## REPOSITORY RENDERERS
53 <%def name="quick_menu(repo_name)">
53 <%def name="quick_menu(repo_name)">
54 <i class="icon-more"></i>
54 <i class="icon-more"></i>
55 <div class="menu_items_container hidden">
55 <div class="menu_items_container hidden">
56 <ul class="menu_items">
56 <ul class="menu_items">
57 <li>
57 <li>
58 <a title="${_('Summary')}" href="${h.route_path('repo_summary',repo_name=repo_name)}">
58 <a title="${_('Summary')}" href="${h.route_path('repo_summary',repo_name=repo_name)}">
59 <span>${_('Summary')}</span>
59 <span>${_('Summary')}</span>
60 </a>
60 </a>
61 </li>
61 </li>
62 <li>
62 <li>
63 <a title="${_('Commits')}" href="${h.route_path('repo_commits',repo_name=repo_name)}">
63 <a title="${_('Commits')}" href="${h.route_path('repo_commits',repo_name=repo_name)}">
64 <span>${_('Commits')}</span>
64 <span>${_('Commits')}</span>
65 </a>
65 </a>
66 </li>
66 </li>
67 <li>
67 <li>
68 <a title="${_('Files')}" href="${h.route_path('repo_files:default_commit',repo_name=repo_name)}">
68 <a title="${_('Files')}" href="${h.route_path('repo_files:default_commit',repo_name=repo_name)}">
69 <span>${_('Files')}</span>
69 <span>${_('Files')}</span>
70 </a>
70 </a>
71 </li>
71 </li>
72 <li>
72 <li>
73 <a title="${_('Fork')}" href="${h.route_path('repo_fork_new',repo_name=repo_name)}">
73 <a title="${_('Fork')}" href="${h.route_path('repo_fork_new',repo_name=repo_name)}">
74 <span>${_('Fork')}</span>
74 <span>${_('Fork')}</span>
75 </a>
75 </a>
76 </li>
76 </li>
77 </ul>
77 </ul>
78 </div>
78 </div>
79 </%def>
79 </%def>
80
80
81 <%def name="repo_name(name,rtype,rstate,private,archived,fork_of,short_name=False,admin=False)">
81 <%def name="repo_name(name,rtype,rstate,private,archived,fork_of,short_name=False,admin=False)">
82 <%
82 <%
83 def get_name(name,short_name=short_name):
83 def get_name(name,short_name=short_name):
84 if short_name:
84 if short_name:
85 return name.split('/')[-1]
85 return name.split('/')[-1]
86 else:
86 else:
87 return name
87 return name
88 %>
88 %>
89 <div class="${'repo_state_pending' if rstate == 'repo_state_pending' else ''} truncate">
89 <div class="${'repo_state_pending' if rstate == 'repo_state_pending' else ''} truncate">
90 ##NAME
90 ##NAME
91 <a href="${h.route_path('edit_repo',repo_name=name) if admin else h.route_path('repo_summary',repo_name=name)}">
91 <a href="${h.route_path('edit_repo',repo_name=name) if admin else h.route_path('repo_summary',repo_name=name)}">
92
92
93 ##TYPE OF REPO
93 ##TYPE OF REPO
94 %if h.is_hg(rtype):
94 %if h.is_hg(rtype):
95 <span title="${_('Mercurial repository')}"><i class="icon-hg" style="font-size: 14px;"></i></span>
95 <span title="${_('Mercurial repository')}"><i class="icon-hg" style="font-size: 14px;"></i></span>
96 %elif h.is_git(rtype):
96 %elif h.is_git(rtype):
97 <span title="${_('Git repository')}"><i class="icon-git" style="font-size: 14px"></i></span>
97 <span title="${_('Git repository')}"><i class="icon-git" style="font-size: 14px"></i></span>
98 %elif h.is_svn(rtype):
98 %elif h.is_svn(rtype):
99 <span title="${_('Subversion repository')}"><i class="icon-svn" style="font-size: 14px"></i></span>
99 <span title="${_('Subversion repository')}"><i class="icon-svn" style="font-size: 14px"></i></span>
100 %endif
100 %endif
101
101
102 ##PRIVATE/PUBLIC
102 ##PRIVATE/PUBLIC
103 %if private is True and c.visual.show_private_icon:
103 %if private is True and c.visual.show_private_icon:
104 <i class="icon-lock" title="${_('Private repository')}"></i>
104 <i class="icon-lock" title="${_('Private repository')}"></i>
105 %elif private is False and c.visual.show_public_icon:
105 %elif private is False and c.visual.show_public_icon:
106 <i class="icon-unlock-alt" title="${_('Public repository')}"></i>
106 <i class="icon-unlock-alt" title="${_('Public repository')}"></i>
107 %else:
107 %else:
108 <span></span>
108 <span></span>
109 %endif
109 %endif
110 ${get_name(name)}
110 ${get_name(name)}
111 </a>
111 </a>
112 %if fork_of:
112 %if fork_of:
113 <a href="${h.route_path('repo_summary',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a>
113 <a href="${h.route_path('repo_summary',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a>
114 %endif
114 %endif
115 %if rstate == 'repo_state_pending':
115 %if rstate == 'repo_state_pending':
116 <span class="creation_in_progress tooltip" title="${_('This repository is being created in a background task')}">
116 <span class="creation_in_progress tooltip" title="${_('This repository is being created in a background task')}">
117 (${_('creating...')})
117 (${_('creating...')})
118 </span>
118 </span>
119 %endif
119 %endif
120
120
121 </div>
121 </div>
122 </%def>
122 </%def>
123
123
124 <%def name="repo_desc(description, stylify_metatags)">
124 <%def name="repo_desc(description, stylify_metatags)">
125 <%
125 <%
126 tags, description = h.extract_metatags(description)
126 tags, description = h.extract_metatags(description)
127 %>
127 %>
128
128
129 <div class="truncate-wrap">
129 <div class="truncate-wrap">
130 % if stylify_metatags:
130 % if stylify_metatags:
131 % for tag_type, tag in tags:
131 % for tag_type, tag in tags:
132 ${h.style_metatag(tag_type, tag)|n}
132 ${h.style_metatag(tag_type, tag)|n}
133 % endfor
133 % endfor
134 % endif
134 % endif
135 ${description}
135 ${description}
136 </div>
136 </div>
137
137
138 </%def>
138 </%def>
139
139
140 <%def name="last_change(last_change)">
140 <%def name="last_change(last_change)">
141 ${h.age_component(last_change, time_is_local=True)}
141 ${h.age_component(last_change, time_is_local=True)}
142 </%def>
142 </%def>
143
143
144 <%def name="revision(repo_name, rev, commit_id, author, last_msg, commit_date)">
144 <%def name="revision(repo_name, rev, commit_id, author, last_msg, commit_date)">
145 <div>
145 <div>
146 %if rev >= 0:
146 %if rev >= 0:
147 <code><a class="tooltip-hovercard" data-hovercard-alt=${h.tooltip(last_msg)} data-hovercard-url="${h.route_path('hovercard_repo_commit', repo_name=repo_name, commit_id=commit_id)}" href="${h.route_path('repo_commit',repo_name=repo_name,commit_id=commit_id)}">${'r{}:{}'.format(rev,h.short_id(commit_id))}</a></code>
147 <code><a class="tooltip-hovercard" data-hovercard-alt=${h.tooltip(last_msg)} data-hovercard-url="${h.route_path('hovercard_repo_commit', repo_name=repo_name, commit_id=commit_id)}" href="${h.route_path('repo_commit',repo_name=repo_name,commit_id=commit_id)}">${'r{}:{}'.format(rev,h.short_id(commit_id))}</a></code>
148 %else:
148 %else:
149 ${_('No commits yet')}
149 ${_('No commits yet')}
150 %endif
150 %endif
151 </div>
151 </div>
152 </%def>
152 </%def>
153
153
154 <%def name="rss(name)">
154 <%def name="rss(name)">
155 %if c.rhodecode_user.username != h.DEFAULT_USER:
155 %if c.rhodecode_user.username != h.DEFAULT_USER:
156 <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>
156 <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>
157 %else:
157 %else:
158 <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>
158 <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>
159 %endif
159 %endif
160 </%def>
160 </%def>
161
161
162 <%def name="atom(name)">
162 <%def name="atom(name)">
163 %if c.rhodecode_user.username != h.DEFAULT_USER:
163 %if c.rhodecode_user.username != h.DEFAULT_USER:
164 <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>
164 <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>
165 %else:
165 %else:
166 <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>
166 <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>
167 %endif
167 %endif
168 </%def>
168 </%def>
169
169
170 <%def name="repo_actions(repo_name, super_user=True)">
170 <%def name="repo_actions(repo_name, super_user=True)">
171 <div>
171 <div>
172 <div class="grid_edit">
172 <div class="grid_edit">
173 <a href="${h.route_path('edit_repo',repo_name=repo_name)}" title="${_('Edit')}">
173 <a href="${h.route_path('edit_repo',repo_name=repo_name)}" title="${_('Edit')}">
174 Edit
174 Edit
175 </a>
175 </a>
176 </div>
176 </div>
177 <div class="grid_delete">
177 <div class="grid_delete">
178 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=repo_name), request=request)}
178 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=repo_name), request=request)}
179 <input class="btn btn-link btn-danger" id="remove_${repo_name}" name="remove_${repo_name}"
179 <input class="btn btn-link btn-danger" id="remove_${repo_name}" name="remove_${repo_name}"
180 onclick="submitConfirm(event, this, _gettext('Confirm to delete this repository'), _gettext('Delete'), '${repo_name}')"
180 onclick="submitConfirm(event, this, _gettext('Confirm to delete this repository'), _gettext('Delete'), '${repo_name}')"
181 type="submit" value="Delete"
181 type="submit" value="Delete"
182 >
182 >
183 ${h.end_form()}
183 ${h.end_form()}
184 </div>
184 </div>
185 </div>
185 </div>
186 </%def>
186 </%def>
187
187
188 <%def name="repo_state(repo_state)">
188 <%def name="repo_state(repo_state)">
189 <div>
189 <div>
190 %if repo_state == 'repo_state_pending':
190 %if repo_state == 'repo_state_pending':
191 <div class="tag tag4">${_('Creating')}</div>
191 <div class="tag tag4">${_('Creating')}</div>
192 %elif repo_state == 'repo_state_created':
192 %elif repo_state == 'repo_state_created':
193 <div class="tag tag1">${_('Created')}</div>
193 <div class="tag tag1">${_('Created')}</div>
194 %else:
194 %else:
195 <div class="tag alert2" title="${h.tooltip(repo_state)}">invalid</div>
195 <div class="tag alert2" title="${h.tooltip(repo_state)}">invalid</div>
196 %endif
196 %endif
197 </div>
197 </div>
198 </%def>
198 </%def>
199
199
200
200
201 ## REPO GROUP RENDERERS
201 ## REPO GROUP RENDERERS
202 <%def name="quick_repo_group_menu(repo_group_name)">
202 <%def name="quick_repo_group_menu(repo_group_name)">
203 <i class="icon-more"></i>
203 <i class="icon-more"></i>
204 <div class="menu_items_container hidden">
204 <div class="menu_items_container hidden">
205 <ul class="menu_items">
205 <ul class="menu_items">
206 <li>
206 <li>
207 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">${_('Summary')}</a>
207 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">${_('Summary')}</a>
208 </li>
208 </li>
209
209
210 </ul>
210 </ul>
211 </div>
211 </div>
212 </%def>
212 </%def>
213
213
214 <%def name="repo_group_name(repo_group_name, children_groups=None)">
214 <%def name="repo_group_name(repo_group_name, children_groups=None)">
215 <div>
215 <div>
216 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">
216 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">
217 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
217 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
218 %if children_groups:
218 %if children_groups:
219 ${h.literal(' &raquo; '.join(children_groups))}
219 ${h.literal(' &raquo; '.join(children_groups))}
220 %else:
220 %else:
221 ${repo_group_name}
221 ${repo_group_name}
222 %endif
222 %endif
223 </a>
223 </a>
224 </div>
224 </div>
225 </%def>
225 </%def>
226
226
227 <%def name="repo_group_desc(description, personal, stylify_metatags)">
227 <%def name="repo_group_desc(description, personal, stylify_metatags)">
228
228
229 <%
229 <%
230 if stylify_metatags:
230 if stylify_metatags:
231 tags, description = h.extract_metatags(description)
231 tags, description = h.extract_metatags(description)
232 %>
232 %>
233
233
234 <div class="truncate-wrap">
234 <div class="truncate-wrap">
235 % if personal:
235 % if personal:
236 <div class="metatag" tag="personal">${_('personal')}</div>
236 <div class="metatag" tag="personal">${_('personal')}</div>
237 % endif
237 % endif
238
238
239 % if stylify_metatags:
239 % if stylify_metatags:
240 % for tag_type, tag in tags:
240 % for tag_type, tag in tags:
241 ${h.style_metatag(tag_type, tag)|n}
241 ${h.style_metatag(tag_type, tag)|n}
242 % endfor
242 % endfor
243 % endif
243 % endif
244 ${description}
244 ${description}
245 </div>
245 </div>
246
246
247 </%def>
247 </%def>
248
248
249 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
249 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
250 <div class="grid_edit">
250 <div class="grid_edit">
251 <a href="${h.route_path('edit_repo_group',repo_group_name=repo_group_name)}" title="${_('Edit')}">Edit</a>
251 <a href="${h.route_path('edit_repo_group',repo_group_name=repo_group_name)}" title="${_('Edit')}">Edit</a>
252 </div>
252 </div>
253 <div class="grid_delete">
253 <div class="grid_delete">
254 ${h.secure_form(h.route_path('edit_repo_group_advanced_delete', repo_group_name=repo_group_name), request=request)}
254 ${h.secure_form(h.route_path('edit_repo_group_advanced_delete', repo_group_name=repo_group_name), request=request)}
255 <input class="btn btn-link btn-danger" id="remove_${repo_group_name}" name="remove_${repo_group_name}"
255 <input class="btn btn-link btn-danger" id="remove_${repo_group_name}" name="remove_${repo_group_name}"
256 onclick="submitConfirm(event, this, _gettext('Confirm to delete this repository group'), _gettext('Delete'), '${_ungettext('`{}` with {} repository','`{}` with {} repositories',gr_count).format(repo_group_name, gr_count)}')"
256 onclick="submitConfirm(event, this, _gettext('Confirm to delete this repository group'), _gettext('Delete'), '${_ungettext('`{}` with {} repository','`{}` with {} repositories',gr_count).format(repo_group_name, gr_count)}')"
257 type="submit" value="Delete"
257 type="submit" value="Delete"
258 >
258 >
259 ${h.end_form()}
259 ${h.end_form()}
260 </div>
260 </div>
261 </%def>
261 </%def>
262
262
263
263
264 <%def name="user_actions(user_id, username)">
264 <%def name="user_actions(user_id, username)">
265 <div class="grid_edit">
265 <div class="grid_edit">
266 <a href="${h.route_path('user_edit',user_id=user_id)}" title="${_('Edit')}">
266 <a href="${h.route_path('user_edit',user_id=user_id)}" title="${_('Edit')}">
267 ${_('Edit')}
267 ${_('Edit')}
268 </a>
268 </a>
269 </div>
269 </div>
270 <div class="grid_delete">
270 <div class="grid_delete">
271 ${h.secure_form(h.route_path('user_delete', user_id=user_id), request=request)}
271 ${h.secure_form(h.route_path('user_delete', user_id=user_id), request=request)}
272 <input class="btn btn-link btn-danger" id="remove_user_${user_id}" name="remove_user_${user_id}"
272 <input class="btn btn-link btn-danger" id="remove_user_${user_id}" name="remove_user_${user_id}"
273 onclick="submitConfirm(event, this, _gettext('Confirm to delete this user'), _gettext('Delete'), '${username}')"
273 onclick="submitConfirm(event, this, _gettext('Confirm to delete this user'), _gettext('Delete'), '${username}')"
274 type="submit" value="Delete"
274 type="submit" value="Delete"
275 >
275 >
276 ${h.end_form()}
276 ${h.end_form()}
277 </div>
277 </div>
278 </%def>
278 </%def>
279
279
280 <%def name="user_group_actions(user_group_id, user_group_name)">
280 <%def name="user_group_actions(user_group_id, user_group_name)">
281 <div class="grid_edit">
281 <div class="grid_edit">
282 <a href="${h.route_path('edit_user_group', user_group_id=user_group_id)}" title="${_('Edit')}">Edit</a>
282 <a href="${h.route_path('edit_user_group', user_group_id=user_group_id)}" title="${_('Edit')}">Edit</a>
283 </div>
283 </div>
284 <div class="grid_delete">
284 <div class="grid_delete">
285 ${h.secure_form(h.route_path('user_groups_delete', user_group_id=user_group_id), request=request)}
285 ${h.secure_form(h.route_path('user_groups_delete', user_group_id=user_group_id), request=request)}
286 <input class="btn btn-link btn-danger" id="remove_group_${user_group_id}" name="remove_group_${user_group_id}"
286 <input class="btn btn-link btn-danger" id="remove_group_${user_group_id}" name="remove_group_${user_group_id}"
287 onclick="submitConfirm(event, this, _gettext('Confirm to delete this user group'), _gettext('Delete'), '${user_group_name}')"
287 onclick="submitConfirm(event, this, _gettext('Confirm to delete this user group'), _gettext('Delete'), '${user_group_name}')"
288 type="submit" value="Delete"
288 type="submit" value="Delete"
289 >
289 >
290 ${h.end_form()}
290 ${h.end_form()}
291 </div>
291 </div>
292 </%def>
292 </%def>
293
293
294
294
295 <%def name="user_name(user_id, username)">
295 <%def name="user_name(user_id, username)">
296 ${h.link_to(h.person(username, 'username_or_name_or_email'), h.route_path('user_edit', user_id=user_id))}
296 ${h.link_to(h.person(username, 'username_or_name_or_email'), h.route_path('user_edit', user_id=user_id))}
297 </%def>
297 </%def>
298
298
299 <%def name="user_profile(username)">
299 <%def name="user_profile(username)">
300 ${base.gravatar_with_user(username, 16, tooltip=True)}
300 ${base.gravatar_with_user(username, 16, tooltip=True)}
301 </%def>
301 </%def>
302
302
303 <%def name="user_group_name(user_group_name)">
303 <%def name="user_group_name(user_group_name)">
304 <div>
304 <div>
305 <i class="icon-user-group" title="${_('User group')}"></i>
305 <i class="icon-user-group" title="${_('User group')}"></i>
306 ${h.link_to_group(user_group_name)}
306 ${h.link_to_group(user_group_name)}
307 </div>
307 </div>
308 </%def>
308 </%def>
309
309
310
310
311 ## GISTS
311 ## GISTS
312
312
313 <%def name="gist_gravatar(full_contact)">
313 <%def name="gist_gravatar(full_contact)">
314 <div class="gist_gravatar">
314 <div class="gist_gravatar">
315 ${base.gravatar(full_contact, 30)}
315 ${base.gravatar(full_contact, 30)}
316 </div>
316 </div>
317 </%def>
317 </%def>
318
318
319 <%def name="gist_access_id(gist_access_id, full_contact)">
319 <%def name="gist_access_id(gist_access_id, full_contact)">
320 <div>
320 <div>
321 <code>
321 <code>
322 <a href="${h.route_path('gist_show', gist_id=gist_access_id)}">${gist_access_id}</a>
322 <a href="${h.route_path('gist_show', gist_id=gist_access_id)}">${gist_access_id}</a>
323 </code>
323 </code>
324 </div>
324 </div>
325 </%def>
325 </%def>
326
326
327 <%def name="gist_author(full_contact, created_on, expires)">
327 <%def name="gist_author(full_contact, created_on, expires)">
328 ${base.gravatar_with_user(full_contact, 16, tooltip=True)}
328 ${base.gravatar_with_user(full_contact, 16, tooltip=True)}
329 </%def>
329 </%def>
330
330
331
331
332 <%def name="gist_created(created_on)">
332 <%def name="gist_created(created_on)">
333 <div class="created">
333 <div class="created">
334 ${h.age_component(created_on, time_is_local=True)}
334 ${h.age_component(created_on, time_is_local=True)}
335 </div>
335 </div>
336 </%def>
336 </%def>
337
337
338 <%def name="gist_expires(expires)">
338 <%def name="gist_expires(expires)">
339 <div class="created">
339 <div class="created">
340 %if expires == -1:
340 %if expires == -1:
341 ${_('never')}
341 ${_('never')}
342 %else:
342 %else:
343 ${h.age_component(h.time_to_utcdatetime(expires))}
343 ${h.age_component(h.time_to_utcdatetime(expires))}
344 %endif
344 %endif
345 </div>
345 </div>
346 </%def>
346 </%def>
347
347
348 <%def name="gist_type(gist_type)">
348 <%def name="gist_type(gist_type)">
349 %if gist_type == 'public':
349 %if gist_type == 'public':
350 <span class="tag tag-gist-public disabled">${_('Public Gist')}</span>
350 <span class="tag tag-gist-public disabled">${_('Public Gist')}</span>
351 %else:
351 %else:
352 <span class="tag tag-gist-private disabled">${_('Private Gist')}</span>
352 <span class="tag tag-gist-private disabled">${_('Private Gist')}</span>
353 %endif
353 %endif
354 </%def>
354 </%def>
355
355
356 <%def name="gist_description(gist_description)">
356 <%def name="gist_description(gist_description)">
357 ${gist_description}
357 ${gist_description}
358 </%def>
358 </%def>
359
359
360
360
361 ## PULL REQUESTS GRID RENDERERS
361 ## PULL REQUESTS GRID RENDERERS
362
362
363 <%def name="pullrequest_target_repo(repo_name)">
363 <%def name="pullrequest_target_repo(repo_name)">
364 <div class="truncate">
364 <div class="truncate">
365 ${h.link_to(repo_name,h.route_path('repo_summary',repo_name=repo_name))}
365 ${h.link_to(repo_name,h.route_path('repo_summary',repo_name=repo_name))}
366 </div>
366 </div>
367 </%def>
367 </%def>
368
368
369 <%def name="pullrequest_status(status)">
369 <%def name="pullrequest_status(status)">
370 <i class="icon-circle review-status-${status}"></i>
370 <i class="icon-circle review-status-${status}"></i>
371 </%def>
371 </%def>
372
372
373 <%def name="pullrequest_title(title, description)">
373 <%def name="pullrequest_title(title, description)">
374 ${title}
374 ${title}
375 </%def>
375 </%def>
376
376
377 <%def name="pullrequest_comments(comments_nr)">
377 <%def name="pullrequest_comments(comments_nr)">
378 <i class="icon-comment"></i> ${comments_nr}
378 <i class="icon-comment"></i> ${comments_nr}
379 </%def>
379 </%def>
380
380
381 <%def name="pullrequest_name(pull_request_id, state, is_wip, target_repo_name, short=False)">
381 <%def name="pullrequest_name(pull_request_id, state, is_wip, target_repo_name, short=False)">
382 <code>
382 <a href="${h.route_path('pullrequest_show',repo_name=target_repo_name,pull_request_id=pull_request_id)}">
383 <a href="${h.route_path('pullrequest_show',repo_name=target_repo_name,pull_request_id=pull_request_id)}">
383
384 % if short:
384 % if short:
385 !${pull_request_id}
385 !${pull_request_id}
386 % else:
386 % else:
387 ${_('Pull request !{}').format(pull_request_id)}
387 ${_('Pull request !{}').format(pull_request_id)}
388 % endif
388 % endif
389
389 </a>
390 </code>
390 % if state not in ['created']:
391 % if state not in ['created']:
391 <span class="tag tag-merge-state-${state} tooltip" title="Pull request state is changing">${state}</span>
392 <span class="tag tag-merge-state-${state} tooltip" title="Pull request state is changing">${state}</span>
392 % endif
393 % endif
393
394
394 % if is_wip:
395 % if is_wip:
395 <span class="tag tooltip" title="${_('Work in progress')}">wip</span>
396 <span class="tag tooltip" title="${_('Work in progress')}">wip</span>
396 % endif
397 % endif
397 </a>
398 </%def>
398 </%def>
399
399
400 <%def name="pullrequest_updated_on(updated_on)">
400 <%def name="pullrequest_updated_on(updated_on)">
401 ${h.age_component(h.time_to_utcdatetime(updated_on))}
401 ${h.age_component(h.time_to_utcdatetime(updated_on))}
402 </%def>
402 </%def>
403
403
404 <%def name="pullrequest_author(full_contact)">
404 <%def name="pullrequest_author(full_contact)">
405 ${base.gravatar_with_user(full_contact, 16, tooltip=True)}
405 ${base.gravatar_with_user(full_contact, 16, tooltip=True)}
406 </%def>
406 </%def>
407
407
408
408
409 ## ARTIFACT RENDERERS
409 ## ARTIFACT RENDERERS
410 <%def name="repo_artifact_name(repo_name, file_uid, artifact_display_name)">
410 <%def name="repo_artifact_name(repo_name, file_uid, artifact_display_name)">
411 <a href="${h.route_path('repo_artifacts_get', repo_name=repo_name, uid=file_uid)}">
411 <a href="${h.route_path('repo_artifacts_get', repo_name=repo_name, uid=file_uid)}">
412 ${artifact_display_name or '_EMPTY_NAME_'}
412 ${artifact_display_name or '_EMPTY_NAME_'}
413 </a>
413 </a>
414 </%def>
414 </%def>
415
415
416 <%def name="repo_artifact_uid(repo_name, file_uid)">
416 <%def name="repo_artifact_uid(repo_name, file_uid)">
417 <code>${h.shorter(file_uid, size=24, prefix=True)}</code>
417 <code>${h.shorter(file_uid, size=24, prefix=True)}</code>
418 </%def>
418 </%def>
419
419
420 <%def name="repo_artifact_sha256(artifact_sha256)">
420 <%def name="repo_artifact_sha256(artifact_sha256)">
421 <div class="code">${h.shorter(artifact_sha256, 12)}</div>
421 <div class="code">${h.shorter(artifact_sha256, 12)}</div>
422 </%def>
422 </%def>
423
423
424 <%def name="repo_artifact_actions(repo_name, file_store_id, file_uid)">
424 <%def name="repo_artifact_actions(repo_name, file_store_id, file_uid)">
425 ## <div class="grid_edit">
425 ## <div class="grid_edit">
426 ## <a href="#Edit" title="${_('Edit')}">${_('Edit')}</a>
426 ## <a href="#Edit" title="${_('Edit')}">${_('Edit')}</a>
427 ## </div>
427 ## </div>
428 <div class="grid_edit">
428 <div class="grid_edit">
429 <a href="${h.route_path('repo_artifacts_info', repo_name=repo_name, uid=file_store_id)}" title="${_('Info')}">${_('Info')}</a>
429 <a href="${h.route_path('repo_artifacts_info', repo_name=repo_name, uid=file_store_id)}" title="${_('Info')}">${_('Info')}</a>
430 </div>
430 </div>
431 % if h.HasRepoPermissionAny('repository.admin')(c.repo_name):
431 % if h.HasRepoPermissionAny('repository.admin')(c.repo_name):
432 <div class="grid_delete">
432 <div class="grid_delete">
433 ${h.secure_form(h.route_path('repo_artifacts_delete', repo_name=repo_name, uid=file_store_id), request=request)}
433 ${h.secure_form(h.route_path('repo_artifacts_delete', repo_name=repo_name, uid=file_store_id), request=request)}
434 <input class="btn btn-link btn-danger" id="remove_artifact_${file_store_id}" name="remove_artifact_${file_store_id}"
434 <input class="btn btn-link btn-danger" id="remove_artifact_${file_store_id}" name="remove_artifact_${file_store_id}"
435 onclick="submitConfirm(event, this, _gettext('Confirm to delete this artifact'), _gettext('Delete'), '${file_uid}')"
435 onclick="submitConfirm(event, this, _gettext('Confirm to delete this artifact'), _gettext('Delete'), '${file_uid}')"
436 type="submit" value="${_('Delete')}"
436 type="submit" value="${_('Delete')}"
437 >
437 >
438 ${h.end_form()}
438 ${h.end_form()}
439 </div>
439 </div>
440 % endif
440 % endif
441 </%def>
441 </%def>
442
442
443 <%def name="markup_form(form_id, form_text='', help_text=None)">
443 <%def name="markup_form(form_id, form_text='', help_text=None)">
444
444
445 <div class="markup-form">
445 <div class="markup-form">
446 <div class="markup-form-area">
446 <div class="markup-form-area">
447 <div class="markup-form-area-header">
447 <div class="markup-form-area-header">
448 <ul class="nav-links clearfix">
448 <ul class="nav-links clearfix">
449 <li class="active">
449 <li class="active">
450 <a href="#edit-text" tabindex="-1" id="edit-btn_${form_id}">${_('Write')}</a>
450 <a href="#edit-text" tabindex="-1" id="edit-btn_${form_id}">${_('Write')}</a>
451 </li>
451 </li>
452 <li class="">
452 <li class="">
453 <a href="#preview-text" tabindex="-1" id="preview-btn_${form_id}">${_('Preview')}</a>
453 <a href="#preview-text" tabindex="-1" id="preview-btn_${form_id}">${_('Preview')}</a>
454 </li>
454 </li>
455 </ul>
455 </ul>
456 </div>
456 </div>
457
457
458 <div class="markup-form-area-write" style="display: block;">
458 <div class="markup-form-area-write" style="display: block;">
459 <div id="edit-container_${form_id}">
459 <div id="edit-container_${form_id}">
460 <textarea id="${form_id}" name="${form_id}" class="comment-block-ta ac-input">${form_text if form_text else ''}</textarea>
460 <textarea id="${form_id}" name="${form_id}" class="comment-block-ta ac-input">${form_text if form_text else ''}</textarea>
461 </div>
461 </div>
462 <div id="preview-container_${form_id}" class="clearfix" style="display: none;">
462 <div id="preview-container_${form_id}" class="clearfix" style="display: none;">
463 <div id="preview-box_${form_id}" class="preview-box"></div>
463 <div id="preview-box_${form_id}" class="preview-box"></div>
464 </div>
464 </div>
465 </div>
465 </div>
466
466
467 <div class="markup-form-area-footer">
467 <div class="markup-form-area-footer">
468 <div class="toolbar">
468 <div class="toolbar">
469 <div class="toolbar-text">
469 <div class="toolbar-text">
470 ${(_('Parsed using %s syntax') % (
470 ${(_('Parsed using %s syntax') % (
471 ('<a href="%s">%s</a>' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
471 ('<a href="%s">%s</a>' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
472 )
472 )
473 )|n}
473 )|n}
474 </div>
474 </div>
475 </div>
475 </div>
476 </div>
476 </div>
477 </div>
477 </div>
478
478
479 <div class="markup-form-footer">
479 <div class="markup-form-footer">
480 % if help_text:
480 % if help_text:
481 <span class="help-block">${help_text}</span>
481 <span class="help-block">${help_text}</span>
482 % endif
482 % endif
483 </div>
483 </div>
484 </div>
484 </div>
485 <script type="text/javascript">
485 <script type="text/javascript">
486 new MarkupForm('${form_id}');
486 new MarkupForm('${form_id}');
487 </script>
487 </script>
488
488
489 </%def>
489 </%def>
General Comments 0
You need to be logged in to leave comments. Login now