Show More
@@ -1,464 +1,468 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | |
|
3 | 3 | # Copyright (C) 2010-2016 RhodeCode GmbH |
|
4 | 4 | # |
|
5 | 5 | # This program is free software: you can redistribute it and/or modify |
|
6 | 6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
7 | 7 | # (only), as published by the Free Software Foundation. |
|
8 | 8 | # |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 | 13 | # |
|
14 | 14 | # You should have received a copy of the GNU Affero General Public License |
|
15 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | 16 | # |
|
17 | 17 | # This program is dual-licensed. If you wish to learn more about the |
|
18 | 18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
19 | 19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
20 | 20 | |
|
21 | 21 | """ |
|
22 | 22 | commit controller for RhodeCode showing changes between commits |
|
23 | 23 | """ |
|
24 | 24 | |
|
25 | 25 | import logging |
|
26 | 26 | |
|
27 | 27 | from collections import defaultdict |
|
28 | 28 | from webob.exc import HTTPForbidden, HTTPBadRequest, HTTPNotFound |
|
29 | 29 | |
|
30 | 30 | from pylons import tmpl_context as c, request, response |
|
31 | 31 | from pylons.i18n.translation import _ |
|
32 | 32 | from pylons.controllers.util import redirect |
|
33 | 33 | |
|
34 | 34 | from rhodecode.lib import auth |
|
35 | 35 | from rhodecode.lib import diffs, codeblocks |
|
36 | 36 | from rhodecode.lib.auth import ( |
|
37 | 37 | LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous) |
|
38 | 38 | from rhodecode.lib.base import BaseRepoController, render |
|
39 | 39 | from rhodecode.lib.compat import OrderedDict |
|
40 | 40 | from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError |
|
41 | 41 | import rhodecode.lib.helpers as h |
|
42 | 42 | from rhodecode.lib.utils import action_logger, jsonify |
|
43 | 43 | from rhodecode.lib.utils2 import safe_unicode |
|
44 | 44 | from rhodecode.lib.vcs.backends.base import EmptyCommit |
|
45 | 45 | from rhodecode.lib.vcs.exceptions import ( |
|
46 | 46 | RepositoryError, CommitDoesNotExistError, NodeDoesNotExistError) |
|
47 | 47 | from rhodecode.model.db import ChangesetComment, ChangesetStatus |
|
48 | 48 | from rhodecode.model.changeset_status import ChangesetStatusModel |
|
49 | 49 | from rhodecode.model.comment import ChangesetCommentsModel |
|
50 | 50 | from rhodecode.model.meta import Session |
|
51 | 51 | from rhodecode.model.repo import RepoModel |
|
52 | 52 | |
|
53 | 53 | |
|
54 | 54 | log = logging.getLogger(__name__) |
|
55 | 55 | |
|
56 | 56 | |
|
57 | 57 | def _update_with_GET(params, GET): |
|
58 | 58 | for k in ['diff1', 'diff2', 'diff']: |
|
59 | 59 | params[k] += GET.getall(k) |
|
60 | 60 | |
|
61 | 61 | |
|
62 | 62 | def get_ignore_ws(fid, GET): |
|
63 | 63 | ig_ws_global = GET.get('ignorews') |
|
64 | 64 | ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid)) |
|
65 | 65 | if ig_ws: |
|
66 | 66 | try: |
|
67 | 67 | return int(ig_ws[0].split(':')[-1]) |
|
68 | 68 | except Exception: |
|
69 | 69 | pass |
|
70 | 70 | return ig_ws_global |
|
71 | 71 | |
|
72 | 72 | |
|
73 | 73 | def _ignorews_url(GET, fileid=None): |
|
74 | 74 | fileid = str(fileid) if fileid else None |
|
75 | 75 | params = defaultdict(list) |
|
76 | 76 | _update_with_GET(params, GET) |
|
77 | 77 | label = _('Show whitespace') |
|
78 | 78 | tooltiplbl = _('Show whitespace for all diffs') |
|
79 | 79 | ig_ws = get_ignore_ws(fileid, GET) |
|
80 | 80 | ln_ctx = get_line_ctx(fileid, GET) |
|
81 | 81 | |
|
82 | 82 | if ig_ws is None: |
|
83 | 83 | params['ignorews'] += [1] |
|
84 | 84 | label = _('Ignore whitespace') |
|
85 | 85 | tooltiplbl = _('Ignore whitespace for all diffs') |
|
86 | 86 | ctx_key = 'context' |
|
87 | 87 | ctx_val = ln_ctx |
|
88 | 88 | |
|
89 | 89 | # if we have passed in ln_ctx pass it along to our params |
|
90 | 90 | if ln_ctx: |
|
91 | 91 | params[ctx_key] += [ctx_val] |
|
92 | 92 | |
|
93 | 93 | if fileid: |
|
94 | 94 | params['anchor'] = 'a_' + fileid |
|
95 | 95 | return h.link_to(label, h.url.current(**params), title=tooltiplbl, class_='tooltip') |
|
96 | 96 | |
|
97 | 97 | |
|
98 | 98 | def get_line_ctx(fid, GET): |
|
99 | 99 | ln_ctx_global = GET.get('context') |
|
100 | 100 | if fid: |
|
101 | 101 | ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid)) |
|
102 | 102 | else: |
|
103 | 103 | _ln_ctx = filter(lambda k: k.startswith('C'), GET) |
|
104 | 104 | ln_ctx = GET.get(_ln_ctx[0]) if _ln_ctx else ln_ctx_global |
|
105 | 105 | if ln_ctx: |
|
106 | 106 | ln_ctx = [ln_ctx] |
|
107 | 107 | |
|
108 | 108 | if ln_ctx: |
|
109 | 109 | retval = ln_ctx[0].split(':')[-1] |
|
110 | 110 | else: |
|
111 | 111 | retval = ln_ctx_global |
|
112 | 112 | |
|
113 | 113 | try: |
|
114 | 114 | return int(retval) |
|
115 | 115 | except Exception: |
|
116 | 116 | return 3 |
|
117 | 117 | |
|
118 | 118 | |
|
119 | 119 | def _context_url(GET, fileid=None): |
|
120 | 120 | """ |
|
121 | 121 | Generates a url for context lines. |
|
122 | 122 | |
|
123 | 123 | :param fileid: |
|
124 | 124 | """ |
|
125 | 125 | |
|
126 | 126 | fileid = str(fileid) if fileid else None |
|
127 | 127 | ig_ws = get_ignore_ws(fileid, GET) |
|
128 | 128 | ln_ctx = (get_line_ctx(fileid, GET) or 3) * 2 |
|
129 | 129 | |
|
130 | 130 | params = defaultdict(list) |
|
131 | 131 | _update_with_GET(params, GET) |
|
132 | 132 | |
|
133 | 133 | if ln_ctx > 0: |
|
134 | 134 | params['context'] += [ln_ctx] |
|
135 | 135 | |
|
136 | 136 | if ig_ws: |
|
137 | 137 | ig_ws_key = 'ignorews' |
|
138 | 138 | ig_ws_val = 1 |
|
139 | 139 | params[ig_ws_key] += [ig_ws_val] |
|
140 | 140 | |
|
141 | 141 | lbl = _('Increase context') |
|
142 | 142 | tooltiplbl = _('Increase context for all diffs') |
|
143 | 143 | |
|
144 | 144 | if fileid: |
|
145 | 145 | params['anchor'] = 'a_' + fileid |
|
146 | 146 | return h.link_to(lbl, h.url.current(**params), title=tooltiplbl, class_='tooltip') |
|
147 | 147 | |
|
148 | 148 | |
|
149 | 149 | class ChangesetController(BaseRepoController): |
|
150 | 150 | |
|
151 | 151 | def __before__(self): |
|
152 | 152 | super(ChangesetController, self).__before__() |
|
153 | 153 | c.affected_files_cut_off = 60 |
|
154 | 154 | |
|
155 | 155 | def _index(self, commit_id_range, method): |
|
156 | 156 | c.ignorews_url = _ignorews_url |
|
157 | 157 | c.context_url = _context_url |
|
158 | 158 | c.fulldiff = fulldiff = request.GET.get('fulldiff') |
|
159 | 159 | |
|
160 | 160 | # fetch global flags of ignore ws or context lines |
|
161 | 161 | context_lcl = get_line_ctx('', request.GET) |
|
162 | 162 | ign_whitespace_lcl = get_ignore_ws('', request.GET) |
|
163 | 163 | |
|
164 | 164 | # diff_limit will cut off the whole diff if the limit is applied |
|
165 | 165 | # otherwise it will just hide the big files from the front-end |
|
166 | 166 | diff_limit = self.cut_off_limit_diff |
|
167 | 167 | file_limit = self.cut_off_limit_file |
|
168 | 168 | |
|
169 | 169 | # get ranges of commit ids if preset |
|
170 | 170 | commit_range = commit_id_range.split('...')[:2] |
|
171 | 171 | |
|
172 | 172 | try: |
|
173 | 173 | pre_load = ['affected_files', 'author', 'branch', 'date', |
|
174 | 174 | 'message', 'parents'] |
|
175 | 175 | |
|
176 | 176 | if len(commit_range) == 2: |
|
177 | 177 | commits = c.rhodecode_repo.get_commits( |
|
178 | 178 | start_id=commit_range[0], end_id=commit_range[1], |
|
179 | 179 | pre_load=pre_load) |
|
180 | 180 | commits = list(commits) |
|
181 | 181 | else: |
|
182 | 182 | commits = [c.rhodecode_repo.get_commit( |
|
183 | 183 | commit_id=commit_id_range, pre_load=pre_load)] |
|
184 | 184 | |
|
185 | 185 | c.commit_ranges = commits |
|
186 | 186 | if not c.commit_ranges: |
|
187 | 187 | raise RepositoryError( |
|
188 | 188 | 'The commit range returned an empty result') |
|
189 | 189 | except CommitDoesNotExistError: |
|
190 | 190 | msg = _('No such commit exists for this repository') |
|
191 | 191 | h.flash(msg, category='error') |
|
192 | 192 | raise HTTPNotFound() |
|
193 | 193 | except Exception: |
|
194 | 194 | log.exception("General failure") |
|
195 | 195 | raise HTTPNotFound() |
|
196 | 196 | |
|
197 | 197 | c.changes = OrderedDict() |
|
198 | 198 | c.lines_added = 0 |
|
199 | 199 | c.lines_deleted = 0 |
|
200 | 200 | |
|
201 | # auto collapse if we have more than limit | |
|
202 | collapse_limit = diffs.DiffProcessor._collapse_commits_over | |
|
203 | c.collapse_all_commits = len(c.commit_ranges) > collapse_limit | |
|
204 | ||
|
201 | 205 | c.commit_statuses = ChangesetStatus.STATUSES |
|
202 | 206 | c.inline_comments = [] |
|
203 | 207 | c.files = [] |
|
204 | 208 | |
|
205 | 209 | c.statuses = [] |
|
206 | 210 | c.comments = [] |
|
207 | 211 | if len(c.commit_ranges) == 1: |
|
208 | 212 | commit = c.commit_ranges[0] |
|
209 | 213 | c.comments = ChangesetCommentsModel().get_comments( |
|
210 | 214 | c.rhodecode_db_repo.repo_id, |
|
211 | 215 | revision=commit.raw_id) |
|
212 | 216 | c.statuses.append(ChangesetStatusModel().get_status( |
|
213 | 217 | c.rhodecode_db_repo.repo_id, commit.raw_id)) |
|
214 | 218 | # comments from PR |
|
215 | 219 | statuses = ChangesetStatusModel().get_statuses( |
|
216 | 220 | c.rhodecode_db_repo.repo_id, commit.raw_id, |
|
217 | 221 | with_revisions=True) |
|
218 | 222 | prs = set(st.pull_request for st in statuses |
|
219 | 223 | if st.pull_request is not None) |
|
220 | 224 | # from associated statuses, check the pull requests, and |
|
221 | 225 | # show comments from them |
|
222 | 226 | for pr in prs: |
|
223 | 227 | c.comments.extend(pr.comments) |
|
224 | 228 | |
|
225 | 229 | # Iterate over ranges (default commit view is always one commit) |
|
226 | 230 | for commit in c.commit_ranges: |
|
227 | 231 | c.changes[commit.raw_id] = [] |
|
228 | 232 | |
|
229 | 233 | commit2 = commit |
|
230 | 234 | commit1 = commit.parents[0] if commit.parents else EmptyCommit() |
|
231 | 235 | |
|
232 | 236 | _diff = c.rhodecode_repo.get_diff( |
|
233 | 237 | commit1, commit2, |
|
234 | 238 | ignore_whitespace=ign_whitespace_lcl, context=context_lcl) |
|
235 | 239 | diff_processor = diffs.DiffProcessor( |
|
236 | 240 | _diff, format='newdiff', diff_limit=diff_limit, |
|
237 | 241 | file_limit=file_limit, show_full_diff=fulldiff) |
|
238 | 242 | |
|
239 | 243 | commit_changes = OrderedDict() |
|
240 | 244 | if method == 'show': |
|
241 | 245 | _parsed = diff_processor.prepare() |
|
242 | 246 | c.limited_diff = isinstance(_parsed, diffs.LimitedDiffContainer) |
|
243 | 247 | |
|
244 | 248 | _parsed = diff_processor.prepare() |
|
245 | 249 | |
|
246 | 250 | def _node_getter(commit): |
|
247 | 251 | def get_node(fname): |
|
248 | 252 | try: |
|
249 | 253 | return commit.get_node(fname) |
|
250 | 254 | except NodeDoesNotExistError: |
|
251 | 255 | return None |
|
252 | 256 | return get_node |
|
253 | 257 | |
|
254 | 258 | inline_comments = ChangesetCommentsModel().get_inline_comments( |
|
255 | 259 | c.rhodecode_db_repo.repo_id, revision=commit.raw_id) |
|
256 | 260 | c.inline_cnt = ChangesetCommentsModel().get_inline_comments_count( |
|
257 | 261 | inline_comments) |
|
258 | 262 | |
|
259 | 263 | diffset = codeblocks.DiffSet( |
|
260 | 264 | repo_name=c.repo_name, |
|
261 | 265 | source_node_getter=_node_getter(commit1), |
|
262 | 266 | target_node_getter=_node_getter(commit2), |
|
263 | 267 | comments=inline_comments |
|
264 | 268 | ).render_patchset(_parsed, commit1.raw_id, commit2.raw_id) |
|
265 | 269 | c.changes[commit.raw_id] = diffset |
|
266 | 270 | else: |
|
267 | 271 | # downloads/raw we only need RAW diff nothing else |
|
268 | 272 | diff = diff_processor.as_raw() |
|
269 | 273 | c.changes[commit.raw_id] = [None, None, None, None, diff, None, None] |
|
270 | 274 | |
|
271 | 275 | # sort comments by how they were generated |
|
272 | 276 | c.comments = sorted(c.comments, key=lambda x: x.comment_id) |
|
273 | 277 | |
|
274 | 278 | |
|
275 | 279 | if len(c.commit_ranges) == 1: |
|
276 | 280 | c.commit = c.commit_ranges[0] |
|
277 | 281 | c.parent_tmpl = ''.join( |
|
278 | 282 | '# Parent %s\n' % x.raw_id for x in c.commit.parents) |
|
279 | 283 | if method == 'download': |
|
280 | 284 | response.content_type = 'text/plain' |
|
281 | 285 | response.content_disposition = ( |
|
282 | 286 | 'attachment; filename=%s.diff' % commit_id_range[:12]) |
|
283 | 287 | return diff |
|
284 | 288 | elif method == 'patch': |
|
285 | 289 | response.content_type = 'text/plain' |
|
286 | 290 | c.diff = safe_unicode(diff) |
|
287 | 291 | return render('changeset/patch_changeset.html') |
|
288 | 292 | elif method == 'raw': |
|
289 | 293 | response.content_type = 'text/plain' |
|
290 | 294 | return diff |
|
291 | 295 | elif method == 'show': |
|
292 | 296 | if len(c.commit_ranges) == 1: |
|
293 | 297 | return render('changeset/changeset.html') |
|
294 | 298 | else: |
|
295 | 299 | c.ancestor = None |
|
296 | 300 | c.target_repo = c.rhodecode_db_repo |
|
297 | 301 | return render('changeset/changeset_range.html') |
|
298 | 302 | |
|
299 | 303 | @LoginRequired() |
|
300 | 304 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
301 | 305 | 'repository.admin') |
|
302 | 306 | def index(self, revision, method='show'): |
|
303 | 307 | return self._index(revision, method=method) |
|
304 | 308 | |
|
305 | 309 | @LoginRequired() |
|
306 | 310 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
307 | 311 | 'repository.admin') |
|
308 | 312 | def changeset_raw(self, revision): |
|
309 | 313 | return self._index(revision, method='raw') |
|
310 | 314 | |
|
311 | 315 | @LoginRequired() |
|
312 | 316 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
313 | 317 | 'repository.admin') |
|
314 | 318 | def changeset_patch(self, revision): |
|
315 | 319 | return self._index(revision, method='patch') |
|
316 | 320 | |
|
317 | 321 | @LoginRequired() |
|
318 | 322 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
319 | 323 | 'repository.admin') |
|
320 | 324 | def changeset_download(self, revision): |
|
321 | 325 | return self._index(revision, method='download') |
|
322 | 326 | |
|
323 | 327 | @LoginRequired() |
|
324 | 328 | @NotAnonymous() |
|
325 | 329 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
326 | 330 | 'repository.admin') |
|
327 | 331 | @auth.CSRFRequired() |
|
328 | 332 | @jsonify |
|
329 | 333 | def comment(self, repo_name, revision): |
|
330 | 334 | commit_id = revision |
|
331 | 335 | status = request.POST.get('changeset_status', None) |
|
332 | 336 | text = request.POST.get('text') |
|
333 | 337 | if status: |
|
334 | 338 | text = text or (_('Status change %(transition_icon)s %(status)s') |
|
335 | 339 | % {'transition_icon': '>', |
|
336 | 340 | 'status': ChangesetStatus.get_status_lbl(status)}) |
|
337 | 341 | |
|
338 | 342 | multi_commit_ids = filter( |
|
339 | 343 | lambda s: s not in ['', None], |
|
340 | 344 | request.POST.get('commit_ids', '').split(','),) |
|
341 | 345 | |
|
342 | 346 | commit_ids = multi_commit_ids or [commit_id] |
|
343 | 347 | comment = None |
|
344 | 348 | for current_id in filter(None, commit_ids): |
|
345 | 349 | c.co = comment = ChangesetCommentsModel().create( |
|
346 | 350 | text=text, |
|
347 | 351 | repo=c.rhodecode_db_repo.repo_id, |
|
348 | 352 | user=c.rhodecode_user.user_id, |
|
349 | 353 | revision=current_id, |
|
350 | 354 | f_path=request.POST.get('f_path'), |
|
351 | 355 | line_no=request.POST.get('line'), |
|
352 | 356 | status_change=(ChangesetStatus.get_status_lbl(status) |
|
353 | 357 | if status else None), |
|
354 | 358 | status_change_type=status |
|
355 | 359 | ) |
|
356 | 360 | # get status if set ! |
|
357 | 361 | if status: |
|
358 | 362 | # if latest status was from pull request and it's closed |
|
359 | 363 | # disallow changing status ! |
|
360 | 364 | # dont_allow_on_closed_pull_request = True ! |
|
361 | 365 | |
|
362 | 366 | try: |
|
363 | 367 | ChangesetStatusModel().set_status( |
|
364 | 368 | c.rhodecode_db_repo.repo_id, |
|
365 | 369 | status, |
|
366 | 370 | c.rhodecode_user.user_id, |
|
367 | 371 | comment, |
|
368 | 372 | revision=current_id, |
|
369 | 373 | dont_allow_on_closed_pull_request=True |
|
370 | 374 | ) |
|
371 | 375 | except StatusChangeOnClosedPullRequestError: |
|
372 | 376 | msg = _('Changing the status of a commit associated with ' |
|
373 | 377 | 'a closed pull request is not allowed') |
|
374 | 378 | log.exception(msg) |
|
375 | 379 | h.flash(msg, category='warning') |
|
376 | 380 | return redirect(h.url( |
|
377 | 381 | 'changeset_home', repo_name=repo_name, |
|
378 | 382 | revision=current_id)) |
|
379 | 383 | |
|
380 | 384 | # finalize, commit and redirect |
|
381 | 385 | Session().commit() |
|
382 | 386 | |
|
383 | 387 | data = { |
|
384 | 388 | 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))), |
|
385 | 389 | } |
|
386 | 390 | if comment: |
|
387 | 391 | data.update(comment.get_dict()) |
|
388 | 392 | data.update({'rendered_text': |
|
389 | 393 | render('changeset/changeset_comment_block.html')}) |
|
390 | 394 | |
|
391 | 395 | return data |
|
392 | 396 | |
|
393 | 397 | @LoginRequired() |
|
394 | 398 | @NotAnonymous() |
|
395 | 399 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
396 | 400 | 'repository.admin') |
|
397 | 401 | @auth.CSRFRequired() |
|
398 | 402 | def preview_comment(self): |
|
399 | 403 | # Technically a CSRF token is not needed as no state changes with this |
|
400 | 404 | # call. However, as this is a POST is better to have it, so automated |
|
401 | 405 | # tools don't flag it as potential CSRF. |
|
402 | 406 | # Post is required because the payload could be bigger than the maximum |
|
403 | 407 | # allowed by GET. |
|
404 | 408 | if not request.environ.get('HTTP_X_PARTIAL_XHR'): |
|
405 | 409 | raise HTTPBadRequest() |
|
406 | 410 | text = request.POST.get('text') |
|
407 | 411 | renderer = request.POST.get('renderer') or 'rst' |
|
408 | 412 | if text: |
|
409 | 413 | return h.render(text, renderer=renderer, mentions=True) |
|
410 | 414 | return '' |
|
411 | 415 | |
|
412 | 416 | @LoginRequired() |
|
413 | 417 | @NotAnonymous() |
|
414 | 418 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
415 | 419 | 'repository.admin') |
|
416 | 420 | @auth.CSRFRequired() |
|
417 | 421 | @jsonify |
|
418 | 422 | def delete_comment(self, repo_name, comment_id): |
|
419 | 423 | comment = ChangesetComment.get(comment_id) |
|
420 | 424 | owner = (comment.author.user_id == c.rhodecode_user.user_id) |
|
421 | 425 | is_repo_admin = h.HasRepoPermissionAny('repository.admin')(c.repo_name) |
|
422 | 426 | if h.HasPermissionAny('hg.admin')() or is_repo_admin or owner: |
|
423 | 427 | ChangesetCommentsModel().delete(comment=comment) |
|
424 | 428 | Session().commit() |
|
425 | 429 | return True |
|
426 | 430 | else: |
|
427 | 431 | raise HTTPForbidden() |
|
428 | 432 | |
|
429 | 433 | @LoginRequired() |
|
430 | 434 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
431 | 435 | 'repository.admin') |
|
432 | 436 | @jsonify |
|
433 | 437 | def changeset_info(self, repo_name, revision): |
|
434 | 438 | if request.is_xhr: |
|
435 | 439 | try: |
|
436 | 440 | return c.rhodecode_repo.get_commit(commit_id=revision) |
|
437 | 441 | except CommitDoesNotExistError as e: |
|
438 | 442 | return EmptyCommit(message=str(e)) |
|
439 | 443 | else: |
|
440 | 444 | raise HTTPBadRequest() |
|
441 | 445 | |
|
442 | 446 | @LoginRequired() |
|
443 | 447 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
444 | 448 | 'repository.admin') |
|
445 | 449 | @jsonify |
|
446 | 450 | def changeset_children(self, repo_name, revision): |
|
447 | 451 | if request.is_xhr: |
|
448 | 452 | commit = c.rhodecode_repo.get_commit(commit_id=revision) |
|
449 | 453 | result = {"results": commit.children} |
|
450 | 454 | return result |
|
451 | 455 | else: |
|
452 | 456 | raise HTTPBadRequest() |
|
453 | 457 | |
|
454 | 458 | @LoginRequired() |
|
455 | 459 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
456 | 460 | 'repository.admin') |
|
457 | 461 | @jsonify |
|
458 | 462 | def changeset_parents(self, repo_name, revision): |
|
459 | 463 | if request.is_xhr: |
|
460 | 464 | commit = c.rhodecode_repo.get_commit(commit_id=revision) |
|
461 | 465 | result = {"results": commit.parents} |
|
462 | 466 | return result |
|
463 | 467 | else: |
|
464 | 468 | raise HTTPBadRequest() |
@@ -1,277 +1,282 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | |
|
3 | 3 | # Copyright (C) 2012-2016 RhodeCode GmbH |
|
4 | 4 | # |
|
5 | 5 | # This program is free software: you can redistribute it and/or modify |
|
6 | 6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
7 | 7 | # (only), as published by the Free Software Foundation. |
|
8 | 8 | # |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 | 13 | # |
|
14 | 14 | # You should have received a copy of the GNU Affero General Public License |
|
15 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | 16 | # |
|
17 | 17 | # This program is dual-licensed. If you wish to learn more about the |
|
18 | 18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
19 | 19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
20 | 20 | |
|
21 | 21 | """ |
|
22 | 22 | Compare controller for showing differences between two commits/refs/tags etc. |
|
23 | 23 | """ |
|
24 | 24 | |
|
25 | 25 | import logging |
|
26 | 26 | |
|
27 | 27 | from webob.exc import HTTPBadRequest |
|
28 | 28 | from pylons import request, tmpl_context as c, url |
|
29 | 29 | from pylons.controllers.util import redirect |
|
30 | 30 | from pylons.i18n.translation import _ |
|
31 | 31 | |
|
32 | 32 | from rhodecode.controllers.utils import parse_path_ref, get_commit_from_ref_name |
|
33 | 33 | from rhodecode.lib import helpers as h |
|
34 | 34 | from rhodecode.lib import diffs, codeblocks |
|
35 | 35 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator |
|
36 | 36 | from rhodecode.lib.base import BaseRepoController, render |
|
37 | 37 | from rhodecode.lib.utils import safe_str |
|
38 | 38 | from rhodecode.lib.utils2 import safe_unicode, str2bool |
|
39 | 39 | from rhodecode.lib.vcs.exceptions import ( |
|
40 | 40 | EmptyRepositoryError, RepositoryError, RepositoryRequirementError, |
|
41 | 41 | NodeDoesNotExistError) |
|
42 | 42 | from rhodecode.model.db import Repository, ChangesetStatus |
|
43 | 43 | |
|
44 | 44 | log = logging.getLogger(__name__) |
|
45 | 45 | |
|
46 | 46 | |
|
47 | 47 | class CompareController(BaseRepoController): |
|
48 | 48 | |
|
49 | 49 | def __before__(self): |
|
50 | 50 | super(CompareController, self).__before__() |
|
51 | 51 | |
|
52 | 52 | def _get_commit_or_redirect( |
|
53 | 53 | self, ref, ref_type, repo, redirect_after=True, partial=False): |
|
54 | 54 | """ |
|
55 | 55 | This is a safe way to get a commit. If an error occurs it |
|
56 | 56 | redirects to a commit with a proper message. If partial is set |
|
57 | 57 | then it does not do redirect raise and throws an exception instead. |
|
58 | 58 | """ |
|
59 | 59 | try: |
|
60 | 60 | return get_commit_from_ref_name(repo, safe_str(ref), ref_type) |
|
61 | 61 | except EmptyRepositoryError: |
|
62 | 62 | if not redirect_after: |
|
63 | 63 | return repo.scm_instance().EMPTY_COMMIT |
|
64 | 64 | h.flash(h.literal(_('There are no commits yet')), |
|
65 | 65 | category='warning') |
|
66 | 66 | redirect(url('summary_home', repo_name=repo.repo_name)) |
|
67 | 67 | |
|
68 | 68 | except RepositoryError as e: |
|
69 | 69 | msg = safe_str(e) |
|
70 | 70 | log.exception(msg) |
|
71 | 71 | h.flash(msg, category='warning') |
|
72 | 72 | if not partial: |
|
73 | 73 | redirect(h.url('summary_home', repo_name=repo.repo_name)) |
|
74 | 74 | raise HTTPBadRequest() |
|
75 | 75 | |
|
76 | 76 | @LoginRequired() |
|
77 | 77 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
78 | 78 | 'repository.admin') |
|
79 | 79 | def index(self, repo_name): |
|
80 | 80 | c.compare_home = True |
|
81 | 81 | c.commit_ranges = [] |
|
82 | c.collapse_all_commits = False | |
|
82 | 83 | c.diffset = None |
|
83 | 84 | c.limited_diff = False |
|
84 | 85 | source_repo = c.rhodecode_db_repo.repo_name |
|
85 | 86 | target_repo = request.GET.get('target_repo', source_repo) |
|
86 | 87 | c.source_repo = Repository.get_by_repo_name(source_repo) |
|
87 | 88 | c.target_repo = Repository.get_by_repo_name(target_repo) |
|
88 | 89 | c.source_ref = c.target_ref = _('Select commit') |
|
89 | 90 | c.source_ref_type = "" |
|
90 | 91 | c.target_ref_type = "" |
|
91 | 92 | c.commit_statuses = ChangesetStatus.STATUSES |
|
92 | 93 | c.preview_mode = False |
|
93 | 94 | c.file_path = None |
|
94 | 95 | return render('compare/compare_diff.html') |
|
95 | 96 | |
|
96 | 97 | @LoginRequired() |
|
97 | 98 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
98 | 99 | 'repository.admin') |
|
99 | 100 | def compare(self, repo_name, source_ref_type, source_ref, |
|
100 | 101 | target_ref_type, target_ref): |
|
101 | 102 | # source_ref will be evaluated in source_repo |
|
102 | 103 | source_repo_name = c.rhodecode_db_repo.repo_name |
|
103 | 104 | source_path, source_id = parse_path_ref(source_ref) |
|
104 | 105 | |
|
105 | 106 | # target_ref will be evaluated in target_repo |
|
106 | 107 | target_repo_name = request.GET.get('target_repo', source_repo_name) |
|
107 | 108 | target_path, target_id = parse_path_ref( |
|
108 | 109 | target_ref, default_path=request.GET.get('f_path', '')) |
|
109 | 110 | |
|
110 | 111 | c.file_path = target_path |
|
111 | 112 | c.commit_statuses = ChangesetStatus.STATUSES |
|
112 | 113 | |
|
113 | 114 | # if merge is True |
|
114 | 115 | # Show what changes since the shared ancestor commit of target/source |
|
115 | 116 | # the source would get if it was merged with target. Only commits |
|
116 | 117 | # which are in target but not in source will be shown. |
|
117 | 118 | merge = str2bool(request.GET.get('merge')) |
|
118 | 119 | # if merge is False |
|
119 | 120 | # Show a raw diff of source/target refs even if no ancestor exists |
|
120 | 121 | |
|
121 | 122 | # c.fulldiff disables cut_off_limit |
|
122 | 123 | c.fulldiff = str2bool(request.GET.get('fulldiff')) |
|
123 | 124 | |
|
124 | 125 | # if partial, returns just compare_commits.html (commits log) |
|
125 | 126 | partial = request.is_xhr |
|
126 | 127 | |
|
127 | 128 | # swap url for compare_diff page |
|
128 | 129 | c.swap_url = h.url( |
|
129 | 130 | 'compare_url', |
|
130 | 131 | repo_name=target_repo_name, |
|
131 | 132 | source_ref_type=target_ref_type, |
|
132 | 133 | source_ref=target_ref, |
|
133 | 134 | target_repo=source_repo_name, |
|
134 | 135 | target_ref_type=source_ref_type, |
|
135 | 136 | target_ref=source_ref, |
|
136 | 137 | merge=merge and '1' or '', |
|
137 | 138 | f_path=target_path) |
|
138 | 139 | |
|
139 | 140 | source_repo = Repository.get_by_repo_name(source_repo_name) |
|
140 | 141 | target_repo = Repository.get_by_repo_name(target_repo_name) |
|
141 | 142 | |
|
142 | 143 | if source_repo is None: |
|
143 | 144 | msg = _('Could not find the original repo: %(repo)s') % { |
|
144 | 145 | 'repo': source_repo} |
|
145 | 146 | |
|
146 | 147 | log.error(msg) |
|
147 | 148 | h.flash(msg, category='error') |
|
148 | 149 | return redirect(url('compare_home', repo_name=c.repo_name)) |
|
149 | 150 | |
|
150 | 151 | if target_repo is None: |
|
151 | 152 | msg = _('Could not find the other repo: %(repo)s') % { |
|
152 | 153 | 'repo': target_repo_name} |
|
153 | 154 | log.error(msg) |
|
154 | 155 | h.flash(msg, category='error') |
|
155 | 156 | return redirect(url('compare_home', repo_name=c.repo_name)) |
|
156 | 157 | |
|
157 | 158 | source_scm = source_repo.scm_instance() |
|
158 | 159 | target_scm = target_repo.scm_instance() |
|
159 | 160 | |
|
160 | 161 | source_alias = source_scm.alias |
|
161 | 162 | target_alias = target_scm.alias |
|
162 | 163 | if source_alias != target_alias: |
|
163 | 164 | msg = _('The comparison of two different kinds of remote repos ' |
|
164 | 165 | 'is not available') |
|
165 | 166 | log.error(msg) |
|
166 | 167 | h.flash(msg, category='error') |
|
167 | 168 | return redirect(url('compare_home', repo_name=c.repo_name)) |
|
168 | 169 | |
|
169 | 170 | source_commit = self._get_commit_or_redirect( |
|
170 | 171 | ref=source_id, ref_type=source_ref_type, repo=source_repo, |
|
171 | 172 | partial=partial) |
|
172 | 173 | target_commit = self._get_commit_or_redirect( |
|
173 | 174 | ref=target_id, ref_type=target_ref_type, repo=target_repo, |
|
174 | 175 | partial=partial) |
|
175 | 176 | |
|
176 | 177 | c.compare_home = False |
|
177 | 178 | c.source_repo = source_repo |
|
178 | 179 | c.target_repo = target_repo |
|
179 | 180 | c.source_ref = source_ref |
|
180 | 181 | c.target_ref = target_ref |
|
181 | 182 | c.source_ref_type = source_ref_type |
|
182 | 183 | c.target_ref_type = target_ref_type |
|
183 | 184 | |
|
184 | 185 | pre_load = ["author", "branch", "date", "message"] |
|
185 | 186 | c.ancestor = None |
|
186 | 187 | |
|
187 | 188 | if c.file_path: |
|
188 | 189 | if source_commit == target_commit: |
|
189 | 190 | c.commit_ranges = [] |
|
190 | 191 | else: |
|
191 | 192 | c.commit_ranges = [target_commit] |
|
192 | 193 | else: |
|
193 | 194 | try: |
|
194 | 195 | c.commit_ranges = source_scm.compare( |
|
195 | 196 | source_commit.raw_id, target_commit.raw_id, |
|
196 | 197 | target_scm, merge, pre_load=pre_load) |
|
197 | 198 | if merge: |
|
198 | 199 | c.ancestor = source_scm.get_common_ancestor( |
|
199 | 200 | source_commit.raw_id, target_commit.raw_id, target_scm) |
|
200 | 201 | except RepositoryRequirementError: |
|
201 | 202 | msg = _('Could not compare repos with different ' |
|
202 | 203 | 'large file settings') |
|
203 | 204 | log.error(msg) |
|
204 | 205 | if partial: |
|
205 | 206 | return msg |
|
206 | 207 | h.flash(msg, category='error') |
|
207 | 208 | return redirect(url('compare_home', repo_name=c.repo_name)) |
|
208 | 209 | |
|
209 | 210 | c.statuses = c.rhodecode_db_repo.statuses( |
|
210 | 211 | [x.raw_id for x in c.commit_ranges]) |
|
211 | 212 | |
|
213 | # auto collapse if we have more than limit | |
|
214 | collapse_limit = diffs.DiffProcessor._collapse_commits_over | |
|
215 | c.collapse_all_commits = len(c.commit_ranges) > collapse_limit | |
|
216 | ||
|
212 | 217 | if partial: # for PR ajax commits loader |
|
213 | 218 | if not c.ancestor: |
|
214 | 219 | return '' # cannot merge if there is no ancestor |
|
215 | 220 | return render('compare/compare_commits.html') |
|
216 | 221 | |
|
217 | 222 | if c.ancestor: |
|
218 | 223 | # case we want a simple diff without incoming commits, |
|
219 | 224 | # previewing what will be merged. |
|
220 | 225 | # Make the diff on target repo (which is known to have target_ref) |
|
221 | 226 | log.debug('Using ancestor %s as source_ref instead of %s' |
|
222 | 227 | % (c.ancestor, source_ref)) |
|
223 | 228 | source_repo = target_repo |
|
224 | 229 | source_commit = target_repo.get_commit(commit_id=c.ancestor) |
|
225 | 230 | |
|
226 | 231 | # diff_limit will cut off the whole diff if the limit is applied |
|
227 | 232 | # otherwise it will just hide the big files from the front-end |
|
228 | 233 | diff_limit = self.cut_off_limit_diff |
|
229 | 234 | file_limit = self.cut_off_limit_file |
|
230 | 235 | |
|
231 | 236 | log.debug('calculating diff between ' |
|
232 | 237 | 'source_ref:%s and target_ref:%s for repo `%s`', |
|
233 | 238 | source_commit, target_commit, |
|
234 | 239 | safe_unicode(source_repo.scm_instance().path)) |
|
235 | 240 | |
|
236 | 241 | if source_commit.repository != target_commit.repository: |
|
237 | 242 | msg = _( |
|
238 | 243 | "Repositories unrelated. " |
|
239 | 244 | "Cannot compare commit %(commit1)s from repository %(repo1)s " |
|
240 | 245 | "with commit %(commit2)s from repository %(repo2)s.") % { |
|
241 | 246 | 'commit1': h.show_id(source_commit), |
|
242 | 247 | 'repo1': source_repo.repo_name, |
|
243 | 248 | 'commit2': h.show_id(target_commit), |
|
244 | 249 | 'repo2': target_repo.repo_name, |
|
245 | 250 | } |
|
246 | 251 | h.flash(msg, category='error') |
|
247 | 252 | raise HTTPBadRequest() |
|
248 | 253 | |
|
249 | 254 | txtdiff = source_repo.scm_instance().get_diff( |
|
250 | 255 | commit1=source_commit, commit2=target_commit, |
|
251 | 256 | path=target_path, path1=source_path) |
|
252 | 257 | |
|
253 | 258 | diff_processor = diffs.DiffProcessor( |
|
254 | 259 | txtdiff, format='newdiff', diff_limit=diff_limit, |
|
255 | 260 | file_limit=file_limit, show_full_diff=c.fulldiff) |
|
256 | 261 | _parsed = diff_processor.prepare() |
|
257 | 262 | |
|
258 | 263 | def _node_getter(commit): |
|
259 | 264 | """ Returns a function that returns a node for a commit or None """ |
|
260 | 265 | def get_node(fname): |
|
261 | 266 | try: |
|
262 | 267 | return commit.get_node(fname) |
|
263 | 268 | except NodeDoesNotExistError: |
|
264 | 269 | return None |
|
265 | 270 | return get_node |
|
266 | 271 | |
|
267 | 272 | c.diffset = codeblocks.DiffSet( |
|
268 | 273 | repo_name=source_repo.repo_name, |
|
269 | 274 | source_node_getter=_node_getter(source_commit), |
|
270 | 275 | target_node_getter=_node_getter(target_commit), |
|
271 | 276 | ).render_patchset(_parsed, source_ref, target_ref) |
|
272 | 277 | |
|
273 | 278 | c.preview_mode = merge |
|
274 | 279 | c.source_commit = source_commit |
|
275 | 280 | c.target_commit = target_commit |
|
276 | 281 | |
|
277 | 282 | return render('compare/compare_diff.html') |
@@ -1,1003 +1,1020 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | |
|
3 | 3 | # Copyright (C) 2012-2016 RhodeCode GmbH |
|
4 | 4 | # |
|
5 | 5 | # This program is free software: you can redistribute it and/or modify |
|
6 | 6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
7 | 7 | # (only), as published by the Free Software Foundation. |
|
8 | 8 | # |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 | 13 | # |
|
14 | 14 | # You should have received a copy of the GNU Affero General Public License |
|
15 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | 16 | # |
|
17 | 17 | # This program is dual-licensed. If you wish to learn more about the |
|
18 | 18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
19 | 19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
20 | 20 | |
|
21 | 21 | """ |
|
22 | 22 | pull requests controller for rhodecode for initializing pull requests |
|
23 | 23 | """ |
|
24 | 24 | import types |
|
25 | 25 | |
|
26 | 26 | import peppercorn |
|
27 | 27 | import formencode |
|
28 | 28 | import logging |
|
29 | ||
|
29 | import collections | |
|
30 | 30 | |
|
31 | 31 | from webob.exc import HTTPNotFound, HTTPForbidden, HTTPBadRequest |
|
32 | 32 | from pylons import request, tmpl_context as c, url |
|
33 | 33 | from pylons.controllers.util import redirect |
|
34 | 34 | from pylons.i18n.translation import _ |
|
35 | 35 | from pyramid.threadlocal import get_current_registry |
|
36 | 36 | from sqlalchemy.sql import func |
|
37 | 37 | from sqlalchemy.sql.expression import or_ |
|
38 | 38 | |
|
39 | 39 | from rhodecode import events |
|
40 | 40 | from rhodecode.lib import auth, diffs, helpers as h, codeblocks |
|
41 | 41 | from rhodecode.lib.ext_json import json |
|
42 | 42 | from rhodecode.lib.base import ( |
|
43 | 43 | BaseRepoController, render, vcs_operation_context) |
|
44 | 44 | from rhodecode.lib.auth import ( |
|
45 | 45 | LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous, |
|
46 | 46 | HasAcceptedRepoType, XHRRequired) |
|
47 | 47 | from rhodecode.lib.channelstream import channelstream_request |
|
48 | from rhodecode.lib.compat import OrderedDict | |
|
49 | 48 | from rhodecode.lib.utils import jsonify |
|
50 | 49 | from rhodecode.lib.utils2 import ( |
|
51 | 50 | safe_int, safe_str, str2bool, safe_unicode) |
|
52 | 51 | from rhodecode.lib.vcs.backends.base import ( |
|
53 | 52 | EmptyCommit, UpdateFailureReason, EmptyRepository) |
|
54 | 53 | from rhodecode.lib.vcs.exceptions import ( |
|
55 | 54 | EmptyRepositoryError, CommitDoesNotExistError, RepositoryRequirementError, |
|
56 | 55 | NodeDoesNotExistError) |
|
57 | 56 | |
|
58 | 57 | from rhodecode.model.changeset_status import ChangesetStatusModel |
|
59 | 58 | from rhodecode.model.comment import ChangesetCommentsModel |
|
60 | 59 | from rhodecode.model.db import (PullRequest, ChangesetStatus, ChangesetComment, |
|
61 | 60 | Repository, PullRequestVersion) |
|
62 | 61 | from rhodecode.model.forms import PullRequestForm |
|
63 | 62 | from rhodecode.model.meta import Session |
|
64 | 63 | from rhodecode.model.pull_request import PullRequestModel |
|
65 | 64 | |
|
66 | 65 | log = logging.getLogger(__name__) |
|
67 | 66 | |
|
68 | 67 | |
|
69 | 68 | class PullrequestsController(BaseRepoController): |
|
70 | 69 | def __before__(self): |
|
71 | 70 | super(PullrequestsController, self).__before__() |
|
72 | 71 | |
|
73 |
def _load_compare_data(self, pull_request, inline_comments |
|
|
72 | def _load_compare_data(self, pull_request, inline_comments): | |
|
74 | 73 | """ |
|
75 | 74 | Load context data needed for generating compare diff |
|
76 | 75 | |
|
77 | 76 | :param pull_request: object related to the request |
|
78 | 77 | :param enable_comments: flag to determine if comments are included |
|
79 | 78 | """ |
|
80 | 79 | source_repo = pull_request.source_repo |
|
81 | 80 | source_ref_id = pull_request.source_ref_parts.commit_id |
|
82 | 81 | |
|
83 | 82 | target_repo = pull_request.target_repo |
|
84 | 83 | target_ref_id = pull_request.target_ref_parts.commit_id |
|
85 | 84 | |
|
86 | 85 | # despite opening commits for bookmarks/branches/tags, we always |
|
87 | 86 | # convert this to rev to prevent changes after bookmark or branch change |
|
88 | 87 | c.source_ref_type = 'rev' |
|
89 | 88 | c.source_ref = source_ref_id |
|
90 | 89 | |
|
91 | 90 | c.target_ref_type = 'rev' |
|
92 | 91 | c.target_ref = target_ref_id |
|
93 | 92 | |
|
94 | 93 | c.source_repo = source_repo |
|
95 | 94 | c.target_repo = target_repo |
|
96 | 95 | |
|
97 | 96 | c.fulldiff = bool(request.GET.get('fulldiff')) |
|
98 | 97 | |
|
99 | 98 | # diff_limit is the old behavior, will cut off the whole diff |
|
100 | 99 | # if the limit is applied otherwise will just hide the |
|
101 | 100 | # big files from the front-end |
|
102 | 101 | diff_limit = self.cut_off_limit_diff |
|
103 | 102 | file_limit = self.cut_off_limit_file |
|
104 | 103 | |
|
105 | 104 | pre_load = ["author", "branch", "date", "message"] |
|
106 | 105 | |
|
107 | 106 | c.commit_ranges = [] |
|
108 | 107 | source_commit = EmptyCommit() |
|
109 | 108 | target_commit = EmptyCommit() |
|
110 | 109 | c.missing_requirements = False |
|
111 | 110 | try: |
|
112 | 111 | c.commit_ranges = [ |
|
113 | 112 | source_repo.get_commit(commit_id=rev, pre_load=pre_load) |
|
114 | 113 | for rev in pull_request.revisions] |
|
115 | 114 | |
|
116 | 115 | c.statuses = source_repo.statuses( |
|
117 | 116 | [x.raw_id for x in c.commit_ranges]) |
|
118 | 117 | |
|
119 | 118 | target_commit = source_repo.get_commit( |
|
120 | 119 | commit_id=safe_str(target_ref_id)) |
|
121 | 120 | source_commit = source_repo.get_commit( |
|
122 | 121 | commit_id=safe_str(source_ref_id)) |
|
123 | 122 | except RepositoryRequirementError: |
|
124 | 123 | c.missing_requirements = True |
|
125 | 124 | |
|
125 | # auto collapse if we have more than limit | |
|
126 | collapse_limit = diffs.DiffProcessor._collapse_commits_over | |
|
127 | c.collapse_all_commits = len(c.commit_ranges) > collapse_limit | |
|
128 | ||
|
126 | 129 | c.changes = {} |
|
127 | 130 | c.missing_commits = False |
|
128 | 131 | if (c.missing_requirements or |
|
129 | 132 | isinstance(source_commit, EmptyCommit) or |
|
130 | 133 | source_commit == target_commit): |
|
131 | 134 | _parsed = [] |
|
132 | 135 | c.missing_commits = True |
|
133 | 136 | else: |
|
134 | 137 | vcs_diff = PullRequestModel().get_diff(pull_request) |
|
135 | 138 | diff_processor = diffs.DiffProcessor( |
|
136 | 139 | vcs_diff, format='newdiff', diff_limit=diff_limit, |
|
137 | 140 | file_limit=file_limit, show_full_diff=c.fulldiff) |
|
138 | _parsed = diff_processor.prepare() | |
|
139 | 141 | |
|
140 | commit_changes = OrderedDict() | |
|
141 | 142 | _parsed = diff_processor.prepare() |
|
142 | 143 | c.limited_diff = isinstance(_parsed, diffs.LimitedDiffContainer) |
|
143 | 144 | |
|
144 | _parsed = diff_processor.prepare() | |
|
145 | included_files = {} | |
|
146 | for f in _parsed: | |
|
147 | included_files[f['filename']] = f['stats'] | |
|
148 | ||
|
149 | c.deleted_files = [fname for fname in inline_comments if | |
|
150 | fname not in included_files] | |
|
151 | ||
|
152 | c.deleted_files_comments = collections.defaultdict(dict) | |
|
153 | for fname, per_line_comments in inline_comments.items(): | |
|
154 | if fname in c.deleted_files: | |
|
155 | c.deleted_files_comments[fname]['stats'] = 0 | |
|
156 | c.deleted_files_comments[fname]['comments'] = list() | |
|
157 | for lno, comments in per_line_comments.items(): | |
|
158 | c.deleted_files_comments[fname]['comments'].extend(comments) | |
|
145 | 159 | |
|
146 | 160 | def _node_getter(commit): |
|
147 | 161 | def get_node(fname): |
|
148 | 162 | try: |
|
149 | 163 | return commit.get_node(fname) |
|
150 | 164 | except NodeDoesNotExistError: |
|
151 | 165 | return None |
|
152 | 166 | return get_node |
|
153 | 167 | |
|
154 | 168 | c.diffset = codeblocks.DiffSet( |
|
155 | 169 | repo_name=c.repo_name, |
|
156 | 170 | source_repo_name=c.source_repo.repo_name, |
|
157 | 171 | source_node_getter=_node_getter(target_commit), |
|
158 | 172 | target_node_getter=_node_getter(source_commit), |
|
159 | 173 | comments=inline_comments |
|
160 | 174 | ).render_patchset(_parsed, target_commit.raw_id, source_commit.raw_id) |
|
161 | 175 | |
|
162 | c.included_files = [] | |
|
163 | c.deleted_files = [] | |
|
164 | ||
|
165 | for f in _parsed: | |
|
166 | st = f['stats'] | |
|
167 | fid = h.FID('', f['filename']) | |
|
168 | c.included_files.append(f['filename']) | |
|
169 | ||
|
170 | 176 | def _extract_ordering(self, request): |
|
171 | 177 | column_index = safe_int(request.GET.get('order[0][column]')) |
|
172 | 178 | order_dir = request.GET.get('order[0][dir]', 'desc') |
|
173 | 179 | order_by = request.GET.get( |
|
174 | 180 | 'columns[%s][data][sort]' % column_index, 'name_raw') |
|
175 | 181 | return order_by, order_dir |
|
176 | 182 | |
|
177 | 183 | @LoginRequired() |
|
178 | 184 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
179 | 185 | 'repository.admin') |
|
180 | 186 | @HasAcceptedRepoType('git', 'hg') |
|
181 | 187 | def show_all(self, repo_name): |
|
182 | 188 | # filter types |
|
183 | 189 | c.active = 'open' |
|
184 | 190 | c.source = str2bool(request.GET.get('source')) |
|
185 | 191 | c.closed = str2bool(request.GET.get('closed')) |
|
186 | 192 | c.my = str2bool(request.GET.get('my')) |
|
187 | 193 | c.awaiting_review = str2bool(request.GET.get('awaiting_review')) |
|
188 | 194 | c.awaiting_my_review = str2bool(request.GET.get('awaiting_my_review')) |
|
189 | 195 | c.repo_name = repo_name |
|
190 | 196 | |
|
191 | 197 | opened_by = None |
|
192 | 198 | if c.my: |
|
193 | 199 | c.active = 'my' |
|
194 | 200 | opened_by = [c.rhodecode_user.user_id] |
|
195 | 201 | |
|
196 | 202 | statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN] |
|
197 | 203 | if c.closed: |
|
198 | 204 | c.active = 'closed' |
|
199 | 205 | statuses = [PullRequest.STATUS_CLOSED] |
|
200 | 206 | |
|
201 | 207 | if c.awaiting_review and not c.source: |
|
202 | 208 | c.active = 'awaiting' |
|
203 | 209 | if c.source and not c.awaiting_review: |
|
204 | 210 | c.active = 'source' |
|
205 | 211 | if c.awaiting_my_review: |
|
206 | 212 | c.active = 'awaiting_my' |
|
207 | 213 | |
|
208 | 214 | data = self._get_pull_requests_list( |
|
209 | 215 | repo_name=repo_name, opened_by=opened_by, statuses=statuses) |
|
210 | 216 | if not request.is_xhr: |
|
211 | 217 | c.data = json.dumps(data['data']) |
|
212 | 218 | c.records_total = data['recordsTotal'] |
|
213 | 219 | return render('/pullrequests/pullrequests.html') |
|
214 | 220 | else: |
|
215 | 221 | return json.dumps(data) |
|
216 | 222 | |
|
217 | 223 | def _get_pull_requests_list(self, repo_name, opened_by, statuses): |
|
218 | 224 | # pagination |
|
219 | 225 | start = safe_int(request.GET.get('start'), 0) |
|
220 | 226 | length = safe_int(request.GET.get('length'), c.visual.dashboard_items) |
|
221 | 227 | order_by, order_dir = self._extract_ordering(request) |
|
222 | 228 | |
|
223 | 229 | if c.awaiting_review: |
|
224 | 230 | pull_requests = PullRequestModel().get_awaiting_review( |
|
225 | 231 | repo_name, source=c.source, opened_by=opened_by, |
|
226 | 232 | statuses=statuses, offset=start, length=length, |
|
227 | 233 | order_by=order_by, order_dir=order_dir) |
|
228 | 234 | pull_requests_total_count = PullRequestModel( |
|
229 | 235 | ).count_awaiting_review( |
|
230 | 236 | repo_name, source=c.source, statuses=statuses, |
|
231 | 237 | opened_by=opened_by) |
|
232 | 238 | elif c.awaiting_my_review: |
|
233 | 239 | pull_requests = PullRequestModel().get_awaiting_my_review( |
|
234 | 240 | repo_name, source=c.source, opened_by=opened_by, |
|
235 | 241 | user_id=c.rhodecode_user.user_id, statuses=statuses, |
|
236 | 242 | offset=start, length=length, order_by=order_by, |
|
237 | 243 | order_dir=order_dir) |
|
238 | 244 | pull_requests_total_count = PullRequestModel( |
|
239 | 245 | ).count_awaiting_my_review( |
|
240 | 246 | repo_name, source=c.source, user_id=c.rhodecode_user.user_id, |
|
241 | 247 | statuses=statuses, opened_by=opened_by) |
|
242 | 248 | else: |
|
243 | 249 | pull_requests = PullRequestModel().get_all( |
|
244 | 250 | repo_name, source=c.source, opened_by=opened_by, |
|
245 | 251 | statuses=statuses, offset=start, length=length, |
|
246 | 252 | order_by=order_by, order_dir=order_dir) |
|
247 | 253 | pull_requests_total_count = PullRequestModel().count_all( |
|
248 | 254 | repo_name, source=c.source, statuses=statuses, |
|
249 | 255 | opened_by=opened_by) |
|
250 | 256 | |
|
251 | 257 | from rhodecode.lib.utils import PartialRenderer |
|
252 | 258 | _render = PartialRenderer('data_table/_dt_elements.html') |
|
253 | 259 | data = [] |
|
254 | 260 | for pr in pull_requests: |
|
255 | 261 | comments = ChangesetCommentsModel().get_all_comments( |
|
256 | 262 | c.rhodecode_db_repo.repo_id, pull_request=pr) |
|
257 | 263 | |
|
258 | 264 | data.append({ |
|
259 | 265 | 'name': _render('pullrequest_name', |
|
260 | 266 | pr.pull_request_id, pr.target_repo.repo_name), |
|
261 | 267 | 'name_raw': pr.pull_request_id, |
|
262 | 268 | 'status': _render('pullrequest_status', |
|
263 | 269 | pr.calculated_review_status()), |
|
264 | 270 | 'title': _render( |
|
265 | 271 | 'pullrequest_title', pr.title, pr.description), |
|
266 | 272 | 'description': h.escape(pr.description), |
|
267 | 273 | 'updated_on': _render('pullrequest_updated_on', |
|
268 | 274 | h.datetime_to_time(pr.updated_on)), |
|
269 | 275 | 'updated_on_raw': h.datetime_to_time(pr.updated_on), |
|
270 | 276 | 'created_on': _render('pullrequest_updated_on', |
|
271 | 277 | h.datetime_to_time(pr.created_on)), |
|
272 | 278 | 'created_on_raw': h.datetime_to_time(pr.created_on), |
|
273 | 279 | 'author': _render('pullrequest_author', |
|
274 | 280 | pr.author.full_contact, ), |
|
275 | 281 | 'author_raw': pr.author.full_name, |
|
276 | 282 | 'comments': _render('pullrequest_comments', len(comments)), |
|
277 | 283 | 'comments_raw': len(comments), |
|
278 | 284 | 'closed': pr.is_closed(), |
|
279 | 285 | }) |
|
280 | 286 | # json used to render the grid |
|
281 | 287 | data = ({ |
|
282 | 288 | 'data': data, |
|
283 | 289 | 'recordsTotal': pull_requests_total_count, |
|
284 | 290 | 'recordsFiltered': pull_requests_total_count, |
|
285 | 291 | }) |
|
286 | 292 | return data |
|
287 | 293 | |
|
288 | 294 | @LoginRequired() |
|
289 | 295 | @NotAnonymous() |
|
290 | 296 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
291 | 297 | 'repository.admin') |
|
292 | 298 | @HasAcceptedRepoType('git', 'hg') |
|
293 | 299 | def index(self): |
|
294 | 300 | source_repo = c.rhodecode_db_repo |
|
295 | 301 | |
|
296 | 302 | try: |
|
297 | 303 | source_repo.scm_instance().get_commit() |
|
298 | 304 | except EmptyRepositoryError: |
|
299 | 305 | h.flash(h.literal(_('There are no commits yet')), |
|
300 | 306 | category='warning') |
|
301 | 307 | redirect(url('summary_home', repo_name=source_repo.repo_name)) |
|
302 | 308 | |
|
303 | 309 | commit_id = request.GET.get('commit') |
|
304 | 310 | branch_ref = request.GET.get('branch') |
|
305 | 311 | bookmark_ref = request.GET.get('bookmark') |
|
306 | 312 | |
|
307 | 313 | try: |
|
308 | 314 | source_repo_data = PullRequestModel().generate_repo_data( |
|
309 | 315 | source_repo, commit_id=commit_id, |
|
310 | 316 | branch=branch_ref, bookmark=bookmark_ref) |
|
311 | 317 | except CommitDoesNotExistError as e: |
|
312 | 318 | log.exception(e) |
|
313 | 319 | h.flash(_('Commit does not exist'), 'error') |
|
314 | 320 | redirect(url('pullrequest_home', repo_name=source_repo.repo_name)) |
|
315 | 321 | |
|
316 | 322 | default_target_repo = source_repo |
|
317 | 323 | |
|
318 | 324 | if source_repo.parent: |
|
319 | 325 | parent_vcs_obj = source_repo.parent.scm_instance() |
|
320 | 326 | if parent_vcs_obj and not parent_vcs_obj.is_empty(): |
|
321 | 327 | # change default if we have a parent repo |
|
322 | 328 | default_target_repo = source_repo.parent |
|
323 | 329 | |
|
324 | 330 | target_repo_data = PullRequestModel().generate_repo_data( |
|
325 | 331 | default_target_repo) |
|
326 | 332 | |
|
327 | 333 | selected_source_ref = source_repo_data['refs']['selected_ref'] |
|
328 | 334 | |
|
329 | 335 | title_source_ref = selected_source_ref.split(':', 2)[1] |
|
330 | 336 | c.default_title = PullRequestModel().generate_pullrequest_title( |
|
331 | 337 | source=source_repo.repo_name, |
|
332 | 338 | source_ref=title_source_ref, |
|
333 | 339 | target=default_target_repo.repo_name |
|
334 | 340 | ) |
|
335 | 341 | |
|
336 | 342 | c.default_repo_data = { |
|
337 | 343 | 'source_repo_name': source_repo.repo_name, |
|
338 | 344 | 'source_refs_json': json.dumps(source_repo_data), |
|
339 | 345 | 'target_repo_name': default_target_repo.repo_name, |
|
340 | 346 | 'target_refs_json': json.dumps(target_repo_data), |
|
341 | 347 | } |
|
342 | 348 | c.default_source_ref = selected_source_ref |
|
343 | 349 | |
|
344 | 350 | return render('/pullrequests/pullrequest.html') |
|
345 | 351 | |
|
346 | 352 | @LoginRequired() |
|
347 | 353 | @NotAnonymous() |
|
348 | 354 | @XHRRequired() |
|
349 | 355 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
350 | 356 | 'repository.admin') |
|
351 | 357 | @jsonify |
|
352 | 358 | def get_repo_refs(self, repo_name, target_repo_name): |
|
353 | 359 | repo = Repository.get_by_repo_name(target_repo_name) |
|
354 | 360 | if not repo: |
|
355 | 361 | raise HTTPNotFound |
|
356 | 362 | return PullRequestModel().generate_repo_data(repo) |
|
357 | 363 | |
|
358 | 364 | @LoginRequired() |
|
359 | 365 | @NotAnonymous() |
|
360 | 366 | @XHRRequired() |
|
361 | 367 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
362 | 368 | 'repository.admin') |
|
363 | 369 | @jsonify |
|
364 | 370 | def get_repo_destinations(self, repo_name): |
|
365 | 371 | repo = Repository.get_by_repo_name(repo_name) |
|
366 | 372 | if not repo: |
|
367 | 373 | raise HTTPNotFound |
|
368 | 374 | filter_query = request.GET.get('query') |
|
369 | 375 | |
|
370 | 376 | query = Repository.query() \ |
|
371 | 377 | .order_by(func.length(Repository.repo_name)) \ |
|
372 | 378 | .filter(or_( |
|
373 | 379 | Repository.repo_name == repo.repo_name, |
|
374 | 380 | Repository.fork_id == repo.repo_id)) |
|
375 | 381 | |
|
376 | 382 | if filter_query: |
|
377 | 383 | ilike_expression = u'%{}%'.format(safe_unicode(filter_query)) |
|
378 | 384 | query = query.filter( |
|
379 | 385 | Repository.repo_name.ilike(ilike_expression)) |
|
380 | 386 | |
|
381 | 387 | add_parent = False |
|
382 | 388 | if repo.parent: |
|
383 | 389 | if filter_query in repo.parent.repo_name: |
|
384 | 390 | parent_vcs_obj = repo.parent.scm_instance() |
|
385 | 391 | if parent_vcs_obj and not parent_vcs_obj.is_empty(): |
|
386 | 392 | add_parent = True |
|
387 | 393 | |
|
388 | 394 | limit = 20 - 1 if add_parent else 20 |
|
389 | 395 | all_repos = query.limit(limit).all() |
|
390 | 396 | if add_parent: |
|
391 | 397 | all_repos += [repo.parent] |
|
392 | 398 | |
|
393 | 399 | repos = [] |
|
394 | 400 | for obj in self.scm_model.get_repos(all_repos): |
|
395 | 401 | repos.append({ |
|
396 | 402 | 'id': obj['name'], |
|
397 | 403 | 'text': obj['name'], |
|
398 | 404 | 'type': 'repo', |
|
399 | 405 | 'obj': obj['dbrepo'] |
|
400 | 406 | }) |
|
401 | 407 | |
|
402 | 408 | data = { |
|
403 | 409 | 'more': False, |
|
404 | 410 | 'results': [{ |
|
405 | 411 | 'text': _('Repositories'), |
|
406 | 412 | 'children': repos |
|
407 | 413 | }] if repos else [] |
|
408 | 414 | } |
|
409 | 415 | return data |
|
410 | 416 | |
|
411 | 417 | @LoginRequired() |
|
412 | 418 | @NotAnonymous() |
|
413 | 419 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
414 | 420 | 'repository.admin') |
|
415 | 421 | @HasAcceptedRepoType('git', 'hg') |
|
416 | 422 | @auth.CSRFRequired() |
|
417 | 423 | def create(self, repo_name): |
|
418 | 424 | repo = Repository.get_by_repo_name(repo_name) |
|
419 | 425 | if not repo: |
|
420 | 426 | raise HTTPNotFound |
|
421 | 427 | |
|
422 | 428 | controls = peppercorn.parse(request.POST.items()) |
|
423 | 429 | |
|
424 | 430 | try: |
|
425 | 431 | _form = PullRequestForm(repo.repo_id)().to_python(controls) |
|
426 | 432 | except formencode.Invalid as errors: |
|
427 | 433 | if errors.error_dict.get('revisions'): |
|
428 | 434 | msg = 'Revisions: %s' % errors.error_dict['revisions'] |
|
429 | 435 | elif errors.error_dict.get('pullrequest_title'): |
|
430 | 436 | msg = _('Pull request requires a title with min. 3 chars') |
|
431 | 437 | else: |
|
432 | 438 | msg = _('Error creating pull request: {}').format(errors) |
|
433 | 439 | log.exception(msg) |
|
434 | 440 | h.flash(msg, 'error') |
|
435 | 441 | |
|
436 | 442 | # would rather just go back to form ... |
|
437 | 443 | return redirect(url('pullrequest_home', repo_name=repo_name)) |
|
438 | 444 | |
|
439 | 445 | source_repo = _form['source_repo'] |
|
440 | 446 | source_ref = _form['source_ref'] |
|
441 | 447 | target_repo = _form['target_repo'] |
|
442 | 448 | target_ref = _form['target_ref'] |
|
443 | 449 | commit_ids = _form['revisions'][::-1] |
|
444 | 450 | reviewers = [ |
|
445 | 451 | (r['user_id'], r['reasons']) for r in _form['review_members']] |
|
446 | 452 | |
|
447 | 453 | # find the ancestor for this pr |
|
448 | 454 | source_db_repo = Repository.get_by_repo_name(_form['source_repo']) |
|
449 | 455 | target_db_repo = Repository.get_by_repo_name(_form['target_repo']) |
|
450 | 456 | |
|
451 | 457 | source_scm = source_db_repo.scm_instance() |
|
452 | 458 | target_scm = target_db_repo.scm_instance() |
|
453 | 459 | |
|
454 | 460 | source_commit = source_scm.get_commit(source_ref.split(':')[-1]) |
|
455 | 461 | target_commit = target_scm.get_commit(target_ref.split(':')[-1]) |
|
456 | 462 | |
|
457 | 463 | ancestor = source_scm.get_common_ancestor( |
|
458 | 464 | source_commit.raw_id, target_commit.raw_id, target_scm) |
|
459 | 465 | |
|
460 | 466 | target_ref_type, target_ref_name, __ = _form['target_ref'].split(':') |
|
461 | 467 | target_ref = ':'.join((target_ref_type, target_ref_name, ancestor)) |
|
462 | 468 | |
|
463 | 469 | pullrequest_title = _form['pullrequest_title'] |
|
464 | 470 | title_source_ref = source_ref.split(':', 2)[1] |
|
465 | 471 | if not pullrequest_title: |
|
466 | 472 | pullrequest_title = PullRequestModel().generate_pullrequest_title( |
|
467 | 473 | source=source_repo, |
|
468 | 474 | source_ref=title_source_ref, |
|
469 | 475 | target=target_repo |
|
470 | 476 | ) |
|
471 | 477 | |
|
472 | 478 | description = _form['pullrequest_desc'] |
|
473 | 479 | try: |
|
474 | 480 | pull_request = PullRequestModel().create( |
|
475 | 481 | c.rhodecode_user.user_id, source_repo, source_ref, target_repo, |
|
476 | 482 | target_ref, commit_ids, reviewers, pullrequest_title, |
|
477 | 483 | description |
|
478 | 484 | ) |
|
479 | 485 | Session().commit() |
|
480 | 486 | h.flash(_('Successfully opened new pull request'), |
|
481 | 487 | category='success') |
|
482 | 488 | except Exception as e: |
|
483 | 489 | msg = _('Error occurred during sending pull request') |
|
484 | 490 | log.exception(msg) |
|
485 | 491 | h.flash(msg, category='error') |
|
486 | 492 | return redirect(url('pullrequest_home', repo_name=repo_name)) |
|
487 | 493 | |
|
488 | 494 | return redirect(url('pullrequest_show', repo_name=target_repo, |
|
489 | 495 | pull_request_id=pull_request.pull_request_id)) |
|
490 | 496 | |
|
491 | 497 | @LoginRequired() |
|
492 | 498 | @NotAnonymous() |
|
493 | 499 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
494 | 500 | 'repository.admin') |
|
495 | 501 | @auth.CSRFRequired() |
|
496 | 502 | @jsonify |
|
497 | 503 | def update(self, repo_name, pull_request_id): |
|
498 | 504 | pull_request_id = safe_int(pull_request_id) |
|
499 | 505 | pull_request = PullRequest.get_or_404(pull_request_id) |
|
500 | 506 | # only owner or admin can update it |
|
501 | 507 | allowed_to_update = PullRequestModel().check_user_update( |
|
502 | 508 | pull_request, c.rhodecode_user) |
|
503 | 509 | if allowed_to_update: |
|
504 | 510 | controls = peppercorn.parse(request.POST.items()) |
|
505 | 511 | |
|
506 | 512 | if 'review_members' in controls: |
|
507 | 513 | self._update_reviewers( |
|
508 | 514 | pull_request_id, controls['review_members']) |
|
509 | 515 | elif str2bool(request.POST.get('update_commits', 'false')): |
|
510 | 516 | self._update_commits(pull_request) |
|
511 | 517 | elif str2bool(request.POST.get('close_pull_request', 'false')): |
|
512 | 518 | self._reject_close(pull_request) |
|
513 | 519 | elif str2bool(request.POST.get('edit_pull_request', 'false')): |
|
514 | 520 | self._edit_pull_request(pull_request) |
|
515 | 521 | else: |
|
516 | 522 | raise HTTPBadRequest() |
|
517 | 523 | return True |
|
518 | 524 | raise HTTPForbidden() |
|
519 | 525 | |
|
520 | 526 | def _edit_pull_request(self, pull_request): |
|
521 | 527 | try: |
|
522 | 528 | PullRequestModel().edit( |
|
523 | 529 | pull_request, request.POST.get('title'), |
|
524 | 530 | request.POST.get('description')) |
|
525 | 531 | except ValueError: |
|
526 | 532 | msg = _(u'Cannot update closed pull requests.') |
|
527 | 533 | h.flash(msg, category='error') |
|
528 | 534 | return |
|
529 | 535 | else: |
|
530 | 536 | Session().commit() |
|
531 | 537 | |
|
532 | 538 | msg = _(u'Pull request title & description updated.') |
|
533 | 539 | h.flash(msg, category='success') |
|
534 | 540 | return |
|
535 | 541 | |
|
536 | 542 | def _update_commits(self, pull_request): |
|
537 | 543 | resp = PullRequestModel().update_commits(pull_request) |
|
538 | 544 | |
|
539 | 545 | if resp.executed: |
|
540 | 546 | msg = _( |
|
541 | 547 | u'Pull request updated to "{source_commit_id}" with ' |
|
542 | 548 | u'{count_added} added, {count_removed} removed commits.') |
|
543 | 549 | msg = msg.format( |
|
544 | 550 | source_commit_id=pull_request.source_ref_parts.commit_id, |
|
545 | 551 | count_added=len(resp.changes.added), |
|
546 | 552 | count_removed=len(resp.changes.removed)) |
|
547 | 553 | h.flash(msg, category='success') |
|
548 | 554 | |
|
549 | 555 | registry = get_current_registry() |
|
550 | 556 | rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {}) |
|
551 | 557 | channelstream_config = rhodecode_plugins.get('channelstream', {}) |
|
552 | 558 | if channelstream_config.get('enabled'): |
|
553 | 559 | message = msg + ( |
|
554 | 560 | ' - <a onclick="window.location.reload()">' |
|
555 | 561 | '<strong>{}</strong></a>'.format(_('Reload page'))) |
|
556 | 562 | channel = '/repo${}$/pr/{}'.format( |
|
557 | 563 | pull_request.target_repo.repo_name, |
|
558 | 564 | pull_request.pull_request_id |
|
559 | 565 | ) |
|
560 | 566 | payload = { |
|
561 | 567 | 'type': 'message', |
|
562 | 568 | 'user': 'system', |
|
563 | 569 | 'exclude_users': [request.user.username], |
|
564 | 570 | 'channel': channel, |
|
565 | 571 | 'message': { |
|
566 | 572 | 'message': message, |
|
567 | 573 | 'level': 'success', |
|
568 | 574 | 'topic': '/notifications' |
|
569 | 575 | } |
|
570 | 576 | } |
|
571 | 577 | channelstream_request( |
|
572 | 578 | channelstream_config, [payload], '/message', |
|
573 | 579 | raise_exc=False) |
|
574 | 580 | else: |
|
575 | 581 | msg = PullRequestModel.UPDATE_STATUS_MESSAGES[resp.reason] |
|
576 | 582 | warning_reasons = [ |
|
577 | 583 | UpdateFailureReason.NO_CHANGE, |
|
578 | 584 | UpdateFailureReason.WRONG_REF_TPYE, |
|
579 | 585 | ] |
|
580 | 586 | category = 'warning' if resp.reason in warning_reasons else 'error' |
|
581 | 587 | h.flash(msg, category=category) |
|
582 | 588 | |
|
583 | 589 | @auth.CSRFRequired() |
|
584 | 590 | @LoginRequired() |
|
585 | 591 | @NotAnonymous() |
|
586 | 592 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
587 | 593 | 'repository.admin') |
|
588 | 594 | def merge(self, repo_name, pull_request_id): |
|
589 | 595 | """ |
|
590 | 596 | POST /{repo_name}/pull-request/{pull_request_id} |
|
591 | 597 | |
|
592 | 598 | Merge will perform a server-side merge of the specified |
|
593 | 599 | pull request, if the pull request is approved and mergeable. |
|
594 | 600 | After succesfull merging, the pull request is automatically |
|
595 | 601 | closed, with a relevant comment. |
|
596 | 602 | """ |
|
597 | 603 | pull_request_id = safe_int(pull_request_id) |
|
598 | 604 | pull_request = PullRequest.get_or_404(pull_request_id) |
|
599 | 605 | user = c.rhodecode_user |
|
600 | 606 | |
|
601 | 607 | if self._meets_merge_pre_conditions(pull_request, user): |
|
602 | 608 | log.debug("Pre-conditions checked, trying to merge.") |
|
603 | 609 | extras = vcs_operation_context( |
|
604 | 610 | request.environ, repo_name=pull_request.target_repo.repo_name, |
|
605 | 611 | username=user.username, action='push', |
|
606 | 612 | scm=pull_request.target_repo.repo_type) |
|
607 | 613 | self._merge_pull_request(pull_request, user, extras) |
|
608 | 614 | |
|
609 | 615 | return redirect(url( |
|
610 | 616 | 'pullrequest_show', |
|
611 | 617 | repo_name=pull_request.target_repo.repo_name, |
|
612 | 618 | pull_request_id=pull_request.pull_request_id)) |
|
613 | 619 | |
|
614 | 620 | def _meets_merge_pre_conditions(self, pull_request, user): |
|
615 | 621 | if not PullRequestModel().check_user_merge(pull_request, user): |
|
616 | 622 | raise HTTPForbidden() |
|
617 | 623 | |
|
618 | 624 | merge_status, msg = PullRequestModel().merge_status(pull_request) |
|
619 | 625 | if not merge_status: |
|
620 | 626 | log.debug("Cannot merge, not mergeable.") |
|
621 | 627 | h.flash(msg, category='error') |
|
622 | 628 | return False |
|
623 | 629 | |
|
624 | 630 | if (pull_request.calculated_review_status() |
|
625 | 631 | is not ChangesetStatus.STATUS_APPROVED): |
|
626 | 632 | log.debug("Cannot merge, approval is pending.") |
|
627 | 633 | msg = _('Pull request reviewer approval is pending.') |
|
628 | 634 | h.flash(msg, category='error') |
|
629 | 635 | return False |
|
630 | 636 | return True |
|
631 | 637 | |
|
632 | 638 | def _merge_pull_request(self, pull_request, user, extras): |
|
633 | 639 | merge_resp = PullRequestModel().merge( |
|
634 | 640 | pull_request, user, extras=extras) |
|
635 | 641 | |
|
636 | 642 | if merge_resp.executed: |
|
637 | 643 | log.debug("The merge was successful, closing the pull request.") |
|
638 | 644 | PullRequestModel().close_pull_request( |
|
639 | 645 | pull_request.pull_request_id, user) |
|
640 | 646 | Session().commit() |
|
641 | 647 | msg = _('Pull request was successfully merged and closed.') |
|
642 | 648 | h.flash(msg, category='success') |
|
643 | 649 | else: |
|
644 | 650 | log.debug( |
|
645 | 651 | "The merge was not successful. Merge response: %s", |
|
646 | 652 | merge_resp) |
|
647 | 653 | msg = PullRequestModel().merge_status_message( |
|
648 | 654 | merge_resp.failure_reason) |
|
649 | 655 | h.flash(msg, category='error') |
|
650 | 656 | |
|
651 | 657 | def _update_reviewers(self, pull_request_id, review_members): |
|
652 | 658 | reviewers = [ |
|
653 | 659 | (int(r['user_id']), r['reasons']) for r in review_members] |
|
654 | 660 | PullRequestModel().update_reviewers(pull_request_id, reviewers) |
|
655 | 661 | Session().commit() |
|
656 | 662 | |
|
657 | 663 | def _reject_close(self, pull_request): |
|
658 | 664 | if pull_request.is_closed(): |
|
659 | 665 | raise HTTPForbidden() |
|
660 | 666 | |
|
661 | 667 | PullRequestModel().close_pull_request_with_comment( |
|
662 | 668 | pull_request, c.rhodecode_user, c.rhodecode_db_repo) |
|
663 | 669 | Session().commit() |
|
664 | 670 | |
|
665 | 671 | @LoginRequired() |
|
666 | 672 | @NotAnonymous() |
|
667 | 673 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
668 | 674 | 'repository.admin') |
|
669 | 675 | @auth.CSRFRequired() |
|
670 | 676 | @jsonify |
|
671 | 677 | def delete(self, repo_name, pull_request_id): |
|
672 | 678 | pull_request_id = safe_int(pull_request_id) |
|
673 | 679 | pull_request = PullRequest.get_or_404(pull_request_id) |
|
674 | 680 | # only owner can delete it ! |
|
675 | 681 | if pull_request.author.user_id == c.rhodecode_user.user_id: |
|
676 | 682 | PullRequestModel().delete(pull_request) |
|
677 | 683 | Session().commit() |
|
678 | 684 | h.flash(_('Successfully deleted pull request'), |
|
679 | 685 | category='success') |
|
680 | 686 | return redirect(url('my_account_pullrequests')) |
|
681 | 687 | raise HTTPForbidden() |
|
682 | 688 | |
|
683 | 689 | def _get_pr_version(self, pull_request_id, version=None): |
|
684 | 690 | pull_request_id = safe_int(pull_request_id) |
|
685 | 691 | at_version = None |
|
686 | 692 | |
|
687 | 693 | if version and version == 'latest': |
|
688 | 694 | pull_request_ver = PullRequest.get(pull_request_id) |
|
689 | 695 | pull_request_obj = pull_request_ver |
|
690 | 696 | _org_pull_request_obj = pull_request_obj |
|
691 | 697 | at_version = 'latest' |
|
692 | 698 | elif version: |
|
693 | 699 | pull_request_ver = PullRequestVersion.get_or_404(version) |
|
694 | 700 | pull_request_obj = pull_request_ver |
|
695 | 701 | _org_pull_request_obj = pull_request_ver.pull_request |
|
696 | 702 | at_version = pull_request_ver.pull_request_version_id |
|
697 | 703 | else: |
|
698 | 704 | _org_pull_request_obj = pull_request_obj = PullRequest.get_or_404(pull_request_id) |
|
699 | 705 | |
|
700 | 706 | pull_request_display_obj = PullRequest.get_pr_display_object( |
|
701 | 707 | pull_request_obj, _org_pull_request_obj) |
|
702 | 708 | return _org_pull_request_obj, pull_request_obj, \ |
|
703 | 709 | pull_request_display_obj, at_version |
|
704 | 710 | |
|
705 | 711 | def _get_pr_version_changes(self, version, pull_request_latest): |
|
706 | 712 | """ |
|
707 | 713 | Generate changes commits, and diff data based on the current pr version |
|
708 | 714 | """ |
|
709 | 715 | |
|
710 | 716 | #TODO(marcink): save those changes as JSON metadata for chaching later. |
|
711 | 717 | |
|
712 | 718 | # fake the version to add the "initial" state object |
|
713 | 719 | pull_request_initial = PullRequest.get_pr_display_object( |
|
714 | 720 | pull_request_latest, pull_request_latest, |
|
715 | 721 | internal_methods=['get_commit', 'versions']) |
|
716 | 722 | pull_request_initial.revisions = [] |
|
717 | 723 | pull_request_initial.source_repo.get_commit = types.MethodType( |
|
718 | 724 | lambda *a, **k: EmptyCommit(), pull_request_initial) |
|
719 | 725 | pull_request_initial.source_repo.scm_instance = types.MethodType( |
|
720 | 726 | lambda *a, **k: EmptyRepository(), pull_request_initial) |
|
721 | 727 | |
|
722 | 728 | _changes_versions = [pull_request_latest] + \ |
|
723 | 729 | list(reversed(c.versions)) + \ |
|
724 | 730 | [pull_request_initial] |
|
725 | 731 | |
|
726 | 732 | if version == 'latest': |
|
727 | 733 | index = 0 |
|
728 | 734 | else: |
|
729 | 735 | for pos, prver in enumerate(_changes_versions): |
|
730 | 736 | ver = getattr(prver, 'pull_request_version_id', -1) |
|
731 | 737 | if ver == safe_int(version): |
|
732 | 738 | index = pos |
|
733 | 739 | break |
|
734 | 740 | else: |
|
735 | 741 | index = 0 |
|
736 | 742 | |
|
737 | 743 | cur_obj = _changes_versions[index] |
|
738 | 744 | prev_obj = _changes_versions[index + 1] |
|
739 | 745 | |
|
740 | 746 | old_commit_ids = set(prev_obj.revisions) |
|
741 | 747 | new_commit_ids = set(cur_obj.revisions) |
|
742 | 748 | |
|
743 | 749 | changes = PullRequestModel()._calculate_commit_id_changes( |
|
744 | 750 | old_commit_ids, new_commit_ids) |
|
745 | 751 | |
|
746 | 752 | old_diff_data, new_diff_data = PullRequestModel()._generate_update_diffs( |
|
747 | 753 | cur_obj, prev_obj) |
|
748 | 754 | file_changes = PullRequestModel()._calculate_file_changes( |
|
749 | 755 | old_diff_data, new_diff_data) |
|
750 | 756 | return changes, file_changes |
|
751 | 757 | |
|
752 | 758 | @LoginRequired() |
|
753 | 759 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
754 | 760 | 'repository.admin') |
|
755 | 761 | def show(self, repo_name, pull_request_id): |
|
756 | 762 | pull_request_id = safe_int(pull_request_id) |
|
757 | 763 | version = request.GET.get('version') |
|
758 | 764 | |
|
759 | 765 | (pull_request_latest, |
|
760 | 766 | pull_request_at_ver, |
|
761 | 767 | pull_request_display_obj, |
|
762 | 768 | at_version) = self._get_pr_version(pull_request_id, version=version) |
|
763 | 769 | |
|
764 | 770 | c.template_context['pull_request_data']['pull_request_id'] = \ |
|
765 | 771 | pull_request_id |
|
766 | 772 | |
|
767 | 773 | # pull_requests repo_name we opened it against |
|
768 | 774 | # ie. target_repo must match |
|
769 | 775 | if repo_name != pull_request_at_ver.target_repo.repo_name: |
|
770 | 776 | raise HTTPNotFound |
|
771 | 777 | |
|
772 | 778 | c.shadow_clone_url = PullRequestModel().get_shadow_clone_url( |
|
773 | 779 | pull_request_at_ver) |
|
774 | 780 | |
|
775 | 781 | pr_closed = pull_request_latest.is_closed() |
|
776 | 782 | if at_version and not at_version == 'latest': |
|
777 | 783 | c.allowed_to_change_status = False |
|
778 | 784 | c.allowed_to_update = False |
|
779 | 785 | c.allowed_to_merge = False |
|
780 | 786 | c.allowed_to_delete = False |
|
781 | 787 | c.allowed_to_comment = False |
|
782 | 788 | else: |
|
783 | 789 | c.allowed_to_change_status = PullRequestModel(). \ |
|
784 | 790 | check_user_change_status(pull_request_at_ver, c.rhodecode_user) |
|
785 | 791 | c.allowed_to_update = PullRequestModel().check_user_update( |
|
786 | 792 | pull_request_latest, c.rhodecode_user) and not pr_closed |
|
787 | 793 | c.allowed_to_merge = PullRequestModel().check_user_merge( |
|
788 | 794 | pull_request_latest, c.rhodecode_user) and not pr_closed |
|
789 | 795 | c.allowed_to_delete = PullRequestModel().check_user_delete( |
|
790 | 796 | pull_request_latest, c.rhodecode_user) and not pr_closed |
|
791 | 797 | c.allowed_to_comment = not pr_closed |
|
792 | 798 | |
|
793 | 799 | cc_model = ChangesetCommentsModel() |
|
794 | 800 | |
|
795 | 801 | c.pull_request_reviewers = pull_request_at_ver.reviewers_statuses() |
|
796 | 802 | c.pull_request_review_status = pull_request_at_ver.calculated_review_status() |
|
797 | 803 | c.pr_merge_status, c.pr_merge_msg = PullRequestModel().merge_status( |
|
798 | 804 | pull_request_at_ver) |
|
799 | 805 | c.approval_msg = None |
|
800 | 806 | if c.pull_request_review_status != ChangesetStatus.STATUS_APPROVED: |
|
801 | 807 | c.approval_msg = _('Reviewer approval is pending.') |
|
802 | 808 | c.pr_merge_status = False |
|
803 | 809 | |
|
804 | 810 | # inline comments |
|
805 |
|
|
|
806 | c.rhodecode_db_repo.repo_id, | |
|
807 | pull_request=pull_request_id) | |
|
811 | inline_comments = cc_model.get_inline_comments( | |
|
812 | c.rhodecode_db_repo.repo_id, pull_request=pull_request_id) | |
|
813 | ||
|
814 | _inline_cnt, c.inline_versions = cc_model.get_inline_comments_count( | |
|
815 | inline_comments, version=at_version, include_aggregates=True) | |
|
816 | ||
|
817 | c.at_version_num = at_version if at_version and at_version != 'latest' else None | |
|
818 | is_outdated = lambda co: \ | |
|
819 | not c.at_version_num \ | |
|
820 | or co.pull_request_version_id <= c.at_version_num | |
|
808 | 821 | |
|
809 | c.inline_cnt = cc_model.get_inline_comments_count( | |
|
810 | c.inline_comments, version=at_version) | |
|
811 | ||
|
812 | # load compare data into template context | |
|
813 | enable_comments = not pr_closed | |
|
814 | self._load_compare_data( | |
|
815 | pull_request_at_ver, | |
|
816 | c.inline_comments, enable_comments=enable_comments) | |
|
822 | # inline_comments_until_version | |
|
823 | if c.at_version_num: | |
|
824 | # if we use version, then do not show later comments | |
|
825 | # than current version | |
|
826 | paths = collections.defaultdict(lambda: collections.defaultdict(list)) | |
|
827 | for fname, per_line_comments in inline_comments.iteritems(): | |
|
828 | for lno, comments in per_line_comments.iteritems(): | |
|
829 | for co in comments: | |
|
830 | if co.pull_request_version_id and is_outdated(co): | |
|
831 | paths[co.f_path][co.line_no].append(co) | |
|
832 | inline_comments = paths | |
|
817 | 833 | |
|
818 | 834 | # outdated comments |
|
819 | c.outdated_comments = {} | |
|
820 | 835 | c.outdated_cnt = 0 |
|
821 | ||
|
822 | 836 | if ChangesetCommentsModel.use_outdated_comments(pull_request_latest): |
|
823 |
|
|
|
837 | outdated_comments = cc_model.get_outdated_comments( | |
|
824 | 838 | c.rhodecode_db_repo.repo_id, |
|
825 | 839 | pull_request=pull_request_at_ver) |
|
826 | 840 | |
|
827 | 841 | # Count outdated comments and check for deleted files |
|
828 | for file_name, lines in c.outdated_comments.iteritems(): | |
|
842 | is_outdated = lambda co: \ | |
|
843 | not c.at_version_num \ | |
|
844 | or co.pull_request_version_id < c.at_version_num | |
|
845 | for file_name, lines in outdated_comments.iteritems(): | |
|
829 | 846 | for comments in lines.values(): |
|
830 | comments = [comm for comm in comments | |
|
831 | if comm.outdated_at_version(at_version)] | |
|
847 | comments = [comm for comm in comments if is_outdated(comm)] | |
|
832 | 848 | c.outdated_cnt += len(comments) |
|
833 | if file_name not in c.included_files: | |
|
834 | c.deleted_files.append(file_name) | |
|
849 | ||
|
850 | # load compare data into template context | |
|
851 | self._load_compare_data(pull_request_at_ver, inline_comments) | |
|
835 | 852 | |
|
836 | 853 | # this is a hack to properly display links, when creating PR, the |
|
837 | 854 | # compare view and others uses different notation, and |
|
838 | 855 | # compare_commits.html renders links based on the target_repo. |
|
839 | 856 | # We need to swap that here to generate it properly on the html side |
|
840 | 857 | c.target_repo = c.source_repo |
|
841 | 858 | |
|
842 | # comments | |
|
843 |
c.comments = cc_model.get_comments( |
|
|
844 | pull_request=pull_request_id) | |
|
859 | # general comments | |
|
860 | c.comments = cc_model.get_comments( | |
|
861 | c.rhodecode_db_repo.repo_id, pull_request=pull_request_id) | |
|
845 | 862 | |
|
846 | 863 | if c.allowed_to_update: |
|
847 | 864 | force_close = ('forced_closed', _('Close Pull Request')) |
|
848 | 865 | statuses = ChangesetStatus.STATUSES + [force_close] |
|
849 | 866 | else: |
|
850 | 867 | statuses = ChangesetStatus.STATUSES |
|
851 | 868 | c.commit_statuses = statuses |
|
852 | 869 | |
|
853 | 870 | c.ancestor = None # TODO: add ancestor here |
|
854 | 871 | c.pull_request = pull_request_display_obj |
|
855 | 872 | c.pull_request_latest = pull_request_latest |
|
856 | 873 | c.at_version = at_version |
|
857 | 874 | |
|
858 | 875 | c.versions = pull_request_display_obj.versions() |
|
859 | 876 | c.changes = None |
|
860 | 877 | c.file_changes = None |
|
861 | 878 | |
|
862 | c.show_version_changes = 1 | |
|
879 | c.show_version_changes = 1 # control flag, not used yet | |
|
863 | 880 | |
|
864 | 881 | if at_version and c.show_version_changes: |
|
865 | 882 | c.changes, c.file_changes = self._get_pr_version_changes( |
|
866 | 883 | version, pull_request_latest) |
|
867 | 884 | |
|
868 | 885 | return render('/pullrequests/pullrequest_show.html') |
|
869 | 886 | |
|
870 | 887 | @LoginRequired() |
|
871 | 888 | @NotAnonymous() |
|
872 | 889 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
873 | 890 | 'repository.admin') |
|
874 | 891 | @auth.CSRFRequired() |
|
875 | 892 | @jsonify |
|
876 | 893 | def comment(self, repo_name, pull_request_id): |
|
877 | 894 | pull_request_id = safe_int(pull_request_id) |
|
878 | 895 | pull_request = PullRequest.get_or_404(pull_request_id) |
|
879 | 896 | if pull_request.is_closed(): |
|
880 | 897 | raise HTTPForbidden() |
|
881 | 898 | |
|
882 | 899 | # TODO: johbo: Re-think this bit, "approved_closed" does not exist |
|
883 | 900 | # as a changeset status, still we want to send it in one value. |
|
884 | 901 | status = request.POST.get('changeset_status', None) |
|
885 | 902 | text = request.POST.get('text') |
|
886 | 903 | if status and '_closed' in status: |
|
887 | 904 | close_pr = True |
|
888 | 905 | status = status.replace('_closed', '') |
|
889 | 906 | else: |
|
890 | 907 | close_pr = False |
|
891 | 908 | |
|
892 | 909 | forced = (status == 'forced') |
|
893 | 910 | if forced: |
|
894 | 911 | status = 'rejected' |
|
895 | 912 | |
|
896 | 913 | allowed_to_change_status = PullRequestModel().check_user_change_status( |
|
897 | 914 | pull_request, c.rhodecode_user) |
|
898 | 915 | |
|
899 | 916 | if status and allowed_to_change_status: |
|
900 | 917 | message = (_('Status change %(transition_icon)s %(status)s') |
|
901 | 918 | % {'transition_icon': '>', |
|
902 | 919 | 'status': ChangesetStatus.get_status_lbl(status)}) |
|
903 | 920 | if close_pr: |
|
904 | 921 | message = _('Closing with') + ' ' + message |
|
905 | 922 | text = text or message |
|
906 | 923 | comm = ChangesetCommentsModel().create( |
|
907 | 924 | text=text, |
|
908 | 925 | repo=c.rhodecode_db_repo.repo_id, |
|
909 | 926 | user=c.rhodecode_user.user_id, |
|
910 | 927 | pull_request=pull_request_id, |
|
911 | 928 | f_path=request.POST.get('f_path'), |
|
912 | 929 | line_no=request.POST.get('line'), |
|
913 | 930 | status_change=(ChangesetStatus.get_status_lbl(status) |
|
914 | 931 | if status and allowed_to_change_status else None), |
|
915 | 932 | status_change_type=(status |
|
916 | 933 | if status and allowed_to_change_status else None), |
|
917 | 934 | closing_pr=close_pr |
|
918 | 935 | ) |
|
919 | 936 | |
|
920 | 937 | if allowed_to_change_status: |
|
921 | 938 | old_calculated_status = pull_request.calculated_review_status() |
|
922 | 939 | # get status if set ! |
|
923 | 940 | if status: |
|
924 | 941 | ChangesetStatusModel().set_status( |
|
925 | 942 | c.rhodecode_db_repo.repo_id, |
|
926 | 943 | status, |
|
927 | 944 | c.rhodecode_user.user_id, |
|
928 | 945 | comm, |
|
929 | 946 | pull_request=pull_request_id |
|
930 | 947 | ) |
|
931 | 948 | |
|
932 | 949 | Session().flush() |
|
933 | 950 | events.trigger(events.PullRequestCommentEvent(pull_request, comm)) |
|
934 | 951 | # we now calculate the status of pull request, and based on that |
|
935 | 952 | # calculation we set the commits status |
|
936 | 953 | calculated_status = pull_request.calculated_review_status() |
|
937 | 954 | if old_calculated_status != calculated_status: |
|
938 | 955 | PullRequestModel()._trigger_pull_request_hook( |
|
939 | 956 | pull_request, c.rhodecode_user, 'review_status_change') |
|
940 | 957 | |
|
941 | 958 | calculated_status_lbl = ChangesetStatus.get_status_lbl( |
|
942 | 959 | calculated_status) |
|
943 | 960 | |
|
944 | 961 | if close_pr: |
|
945 | 962 | status_completed = ( |
|
946 | 963 | calculated_status in [ChangesetStatus.STATUS_APPROVED, |
|
947 | 964 | ChangesetStatus.STATUS_REJECTED]) |
|
948 | 965 | if forced or status_completed: |
|
949 | 966 | PullRequestModel().close_pull_request( |
|
950 | 967 | pull_request_id, c.rhodecode_user) |
|
951 | 968 | else: |
|
952 | 969 | h.flash(_('Closing pull request on other statuses than ' |
|
953 | 970 | 'rejected or approved is forbidden. ' |
|
954 | 971 | 'Calculated status from all reviewers ' |
|
955 | 972 | 'is currently: %s') % calculated_status_lbl, |
|
956 | 973 | category='warning') |
|
957 | 974 | |
|
958 | 975 | Session().commit() |
|
959 | 976 | |
|
960 | 977 | if not request.is_xhr: |
|
961 | 978 | return redirect(h.url('pullrequest_show', repo_name=repo_name, |
|
962 | 979 | pull_request_id=pull_request_id)) |
|
963 | 980 | |
|
964 | 981 | data = { |
|
965 | 982 | 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))), |
|
966 | 983 | } |
|
967 | 984 | if comm: |
|
968 | 985 | c.co = comm |
|
969 | 986 | data.update(comm.get_dict()) |
|
970 | 987 | data.update({'rendered_text': |
|
971 | 988 | render('changeset/changeset_comment_block.html')}) |
|
972 | 989 | |
|
973 | 990 | return data |
|
974 | 991 | |
|
975 | 992 | @LoginRequired() |
|
976 | 993 | @NotAnonymous() |
|
977 | 994 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
978 | 995 | 'repository.admin') |
|
979 | 996 | @auth.CSRFRequired() |
|
980 | 997 | @jsonify |
|
981 | 998 | def delete_comment(self, repo_name, comment_id): |
|
982 | 999 | return self._delete_comment(comment_id) |
|
983 | 1000 | |
|
984 | 1001 | def _delete_comment(self, comment_id): |
|
985 | 1002 | comment_id = safe_int(comment_id) |
|
986 | 1003 | co = ChangesetComment.get_or_404(comment_id) |
|
987 | 1004 | if co.pull_request.is_closed(): |
|
988 | 1005 | # don't allow deleting comments on closed pull request |
|
989 | 1006 | raise HTTPForbidden() |
|
990 | 1007 | |
|
991 | 1008 | is_owner = co.author.user_id == c.rhodecode_user.user_id |
|
992 | 1009 | is_repo_admin = h.HasRepoPermissionAny('repository.admin')(c.repo_name) |
|
993 | 1010 | if h.HasPermissionAny('hg.admin')() or is_repo_admin or is_owner: |
|
994 | 1011 | old_calculated_status = co.pull_request.calculated_review_status() |
|
995 | 1012 | ChangesetCommentsModel().delete(comment=co) |
|
996 | 1013 | Session().commit() |
|
997 | 1014 | calculated_status = co.pull_request.calculated_review_status() |
|
998 | 1015 | if old_calculated_status != calculated_status: |
|
999 | 1016 | PullRequestModel()._trigger_pull_request_hook( |
|
1000 | 1017 | co.pull_request, c.rhodecode_user, 'review_status_change') |
|
1001 | 1018 | return True |
|
1002 | 1019 | else: |
|
1003 | 1020 | raise HTTPForbidden() |
@@ -1,1161 +1,1164 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | |
|
3 | 3 | # Copyright (C) 2011-2016 RhodeCode GmbH |
|
4 | 4 | # |
|
5 | 5 | # This program is free software: you can redistribute it and/or modify |
|
6 | 6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
7 | 7 | # (only), as published by the Free Software Foundation. |
|
8 | 8 | # |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 | 13 | # |
|
14 | 14 | # You should have received a copy of the GNU Affero General Public License |
|
15 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | 16 | # |
|
17 | 17 | # This program is dual-licensed. If you wish to learn more about the |
|
18 | 18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
19 | 19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
20 | 20 | |
|
21 | 21 | |
|
22 | 22 | """ |
|
23 | 23 | Set of diffing helpers, previously part of vcs |
|
24 | 24 | """ |
|
25 | 25 | |
|
26 | 26 | import collections |
|
27 | 27 | import re |
|
28 | 28 | import difflib |
|
29 | 29 | import logging |
|
30 | 30 | |
|
31 | 31 | from itertools import tee, imap |
|
32 | 32 | |
|
33 | 33 | from pylons.i18n.translation import _ |
|
34 | 34 | |
|
35 | 35 | from rhodecode.lib.vcs.exceptions import VCSError |
|
36 | 36 | from rhodecode.lib.vcs.nodes import FileNode, SubModuleNode |
|
37 | 37 | from rhodecode.lib.vcs.backends.base import EmptyCommit |
|
38 | 38 | from rhodecode.lib.helpers import escape |
|
39 | 39 | from rhodecode.lib.utils2 import safe_unicode |
|
40 | 40 | |
|
41 | 41 | log = logging.getLogger(__name__) |
|
42 | 42 | |
|
43 | 43 | # define max context, a file with more than this numbers of lines is unusable |
|
44 | 44 | # in browser anyway |
|
45 | 45 | MAX_CONTEXT = 1024 * 1014 |
|
46 | 46 | |
|
47 | 47 | |
|
48 | 48 | class OPS(object): |
|
49 | 49 | ADD = 'A' |
|
50 | 50 | MOD = 'M' |
|
51 | 51 | DEL = 'D' |
|
52 | 52 | |
|
53 | 53 | |
|
54 | 54 | def wrap_to_table(str_): |
|
55 | 55 | return '''<table class="code-difftable"> |
|
56 | 56 | <tr class="line no-comment"> |
|
57 | 57 | <td class="add-comment-line tooltip" title="%s"><span class="add-comment-content"></span></td> |
|
58 | 58 | <td></td> |
|
59 | 59 | <td class="lineno new"></td> |
|
60 | 60 | <td class="code no-comment"><pre>%s</pre></td> |
|
61 | 61 | </tr> |
|
62 | 62 | </table>''' % (_('Click to comment'), str_) |
|
63 | 63 | |
|
64 | 64 | |
|
65 | 65 | def wrapped_diff(filenode_old, filenode_new, diff_limit=None, file_limit=None, |
|
66 | 66 | show_full_diff=False, ignore_whitespace=True, line_context=3, |
|
67 | 67 | enable_comments=False): |
|
68 | 68 | """ |
|
69 | 69 | returns a wrapped diff into a table, checks for cut_off_limit for file and |
|
70 | 70 | whole diff and presents proper message |
|
71 | 71 | """ |
|
72 | 72 | |
|
73 | 73 | if filenode_old is None: |
|
74 | 74 | filenode_old = FileNode(filenode_new.path, '', EmptyCommit()) |
|
75 | 75 | |
|
76 | 76 | if filenode_old.is_binary or filenode_new.is_binary: |
|
77 | 77 | diff = wrap_to_table(_('Binary file')) |
|
78 | 78 | stats = None |
|
79 | 79 | size = 0 |
|
80 | 80 | data = None |
|
81 | 81 | |
|
82 | 82 | elif diff_limit != -1 and (diff_limit is None or |
|
83 | 83 | (filenode_old.size < diff_limit and filenode_new.size < diff_limit)): |
|
84 | 84 | |
|
85 | 85 | f_gitdiff = get_gitdiff(filenode_old, filenode_new, |
|
86 | 86 | ignore_whitespace=ignore_whitespace, |
|
87 | 87 | context=line_context) |
|
88 | 88 | diff_processor = DiffProcessor( |
|
89 | 89 | f_gitdiff, format='gitdiff', diff_limit=diff_limit, |
|
90 | 90 | file_limit=file_limit, show_full_diff=show_full_diff) |
|
91 | 91 | _parsed = diff_processor.prepare() |
|
92 | 92 | |
|
93 | 93 | diff = diff_processor.as_html(enable_comments=enable_comments) |
|
94 | 94 | stats = _parsed[0]['stats'] if _parsed else None |
|
95 | 95 | size = len(diff or '') |
|
96 | 96 | data = _parsed[0] if _parsed else None |
|
97 | 97 | else: |
|
98 | 98 | diff = wrap_to_table(_('Changeset was too big and was cut off, use ' |
|
99 | 99 | 'diff menu to display this diff')) |
|
100 | 100 | stats = None |
|
101 | 101 | size = 0 |
|
102 | 102 | data = None |
|
103 | 103 | if not diff: |
|
104 | 104 | submodules = filter(lambda o: isinstance(o, SubModuleNode), |
|
105 | 105 | [filenode_new, filenode_old]) |
|
106 | 106 | if submodules: |
|
107 | 107 | diff = wrap_to_table(escape('Submodule %r' % submodules[0])) |
|
108 | 108 | else: |
|
109 | 109 | diff = wrap_to_table(_('No changes detected')) |
|
110 | 110 | |
|
111 | 111 | cs1 = filenode_old.commit.raw_id |
|
112 | 112 | cs2 = filenode_new.commit.raw_id |
|
113 | 113 | |
|
114 | 114 | return size, cs1, cs2, diff, stats, data |
|
115 | 115 | |
|
116 | 116 | |
|
117 | 117 | def get_gitdiff(filenode_old, filenode_new, ignore_whitespace=True, context=3): |
|
118 | 118 | """ |
|
119 | 119 | Returns git style diff between given ``filenode_old`` and ``filenode_new``. |
|
120 | 120 | |
|
121 | 121 | :param ignore_whitespace: ignore whitespaces in diff |
|
122 | 122 | """ |
|
123 | 123 | # make sure we pass in default context |
|
124 | 124 | context = context or 3 |
|
125 | 125 | # protect against IntOverflow when passing HUGE context |
|
126 | 126 | if context > MAX_CONTEXT: |
|
127 | 127 | context = MAX_CONTEXT |
|
128 | 128 | |
|
129 | 129 | submodules = filter(lambda o: isinstance(o, SubModuleNode), |
|
130 | 130 | [filenode_new, filenode_old]) |
|
131 | 131 | if submodules: |
|
132 | 132 | return '' |
|
133 | 133 | |
|
134 | 134 | for filenode in (filenode_old, filenode_new): |
|
135 | 135 | if not isinstance(filenode, FileNode): |
|
136 | 136 | raise VCSError( |
|
137 | 137 | "Given object should be FileNode object, not %s" |
|
138 | 138 | % filenode.__class__) |
|
139 | 139 | |
|
140 | 140 | repo = filenode_new.commit.repository |
|
141 | 141 | old_commit = filenode_old.commit or repo.EMPTY_COMMIT |
|
142 | 142 | new_commit = filenode_new.commit |
|
143 | 143 | |
|
144 | 144 | vcs_gitdiff = repo.get_diff( |
|
145 | 145 | old_commit, new_commit, filenode_new.path, |
|
146 | 146 | ignore_whitespace, context, path1=filenode_old.path) |
|
147 | 147 | return vcs_gitdiff |
|
148 | 148 | |
|
149 | 149 | NEW_FILENODE = 1 |
|
150 | 150 | DEL_FILENODE = 2 |
|
151 | 151 | MOD_FILENODE = 3 |
|
152 | 152 | RENAMED_FILENODE = 4 |
|
153 | 153 | COPIED_FILENODE = 5 |
|
154 | 154 | CHMOD_FILENODE = 6 |
|
155 | 155 | BIN_FILENODE = 7 |
|
156 | 156 | |
|
157 | 157 | |
|
158 | 158 | class LimitedDiffContainer(object): |
|
159 | 159 | |
|
160 | 160 | def __init__(self, diff_limit, cur_diff_size, diff): |
|
161 | 161 | self.diff = diff |
|
162 | 162 | self.diff_limit = diff_limit |
|
163 | 163 | self.cur_diff_size = cur_diff_size |
|
164 | 164 | |
|
165 | 165 | def __getitem__(self, key): |
|
166 | 166 | return self.diff.__getitem__(key) |
|
167 | 167 | |
|
168 | 168 | def __iter__(self): |
|
169 | 169 | for l in self.diff: |
|
170 | 170 | yield l |
|
171 | 171 | |
|
172 | 172 | |
|
173 | 173 | class Action(object): |
|
174 | 174 | """ |
|
175 | 175 | Contains constants for the action value of the lines in a parsed diff. |
|
176 | 176 | """ |
|
177 | 177 | |
|
178 | 178 | ADD = 'add' |
|
179 | 179 | DELETE = 'del' |
|
180 | 180 | UNMODIFIED = 'unmod' |
|
181 | 181 | |
|
182 | 182 | CONTEXT = 'context' |
|
183 | 183 | OLD_NO_NL = 'old-no-nl' |
|
184 | 184 | NEW_NO_NL = 'new-no-nl' |
|
185 | 185 | |
|
186 | 186 | |
|
187 | 187 | class DiffProcessor(object): |
|
188 | 188 | """ |
|
189 | 189 | Give it a unified or git diff and it returns a list of the files that were |
|
190 | 190 | mentioned in the diff together with a dict of meta information that |
|
191 | 191 | can be used to render it in a HTML template. |
|
192 | 192 | |
|
193 | 193 | .. note:: Unicode handling |
|
194 | 194 | |
|
195 | 195 | The original diffs are a byte sequence and can contain filenames |
|
196 | 196 | in mixed encodings. This class generally returns `unicode` objects |
|
197 | 197 | since the result is intended for presentation to the user. |
|
198 | 198 | |
|
199 | 199 | """ |
|
200 | 200 | _chunk_re = re.compile(r'^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)') |
|
201 | 201 | _newline_marker = re.compile(r'^\\ No newline at end of file') |
|
202 | 202 | |
|
203 | 203 | # used for inline highlighter word split |
|
204 | 204 | _token_re = re.compile(r'()(>|<|&|\W+?)') |
|
205 | 205 | |
|
206 | # collapse ranges of commits over given number | |
|
207 | _collapse_commits_over = 5 | |
|
208 | ||
|
206 | 209 | def __init__(self, diff, format='gitdiff', diff_limit=None, |
|
207 | 210 | file_limit=None, show_full_diff=True): |
|
208 | 211 | """ |
|
209 | 212 | :param diff: A `Diff` object representing a diff from a vcs backend |
|
210 | 213 | :param format: format of diff passed, `udiff` or `gitdiff` |
|
211 | 214 | :param diff_limit: define the size of diff that is considered "big" |
|
212 | 215 | based on that parameter cut off will be triggered, set to None |
|
213 | 216 | to show full diff |
|
214 | 217 | """ |
|
215 | 218 | self._diff = diff |
|
216 | 219 | self._format = format |
|
217 | 220 | self.adds = 0 |
|
218 | 221 | self.removes = 0 |
|
219 | 222 | # calculate diff size |
|
220 | 223 | self.diff_limit = diff_limit |
|
221 | 224 | self.file_limit = file_limit |
|
222 | 225 | self.show_full_diff = show_full_diff |
|
223 | 226 | self.cur_diff_size = 0 |
|
224 | 227 | self.parsed = False |
|
225 | 228 | self.parsed_diff = [] |
|
226 | 229 | |
|
227 | 230 | if format == 'gitdiff': |
|
228 | 231 | self.differ = self._highlight_line_difflib |
|
229 | 232 | self._parser = self._parse_gitdiff |
|
230 | 233 | else: |
|
231 | 234 | self.differ = self._highlight_line_udiff |
|
232 | 235 | self._parser = self._new_parse_gitdiff |
|
233 | 236 | |
|
234 | 237 | def _copy_iterator(self): |
|
235 | 238 | """ |
|
236 | 239 | make a fresh copy of generator, we should not iterate thru |
|
237 | 240 | an original as it's needed for repeating operations on |
|
238 | 241 | this instance of DiffProcessor |
|
239 | 242 | """ |
|
240 | 243 | self.__udiff, iterator_copy = tee(self.__udiff) |
|
241 | 244 | return iterator_copy |
|
242 | 245 | |
|
243 | 246 | def _escaper(self, string): |
|
244 | 247 | """ |
|
245 | 248 | Escaper for diff escapes special chars and checks the diff limit |
|
246 | 249 | |
|
247 | 250 | :param string: |
|
248 | 251 | """ |
|
249 | 252 | |
|
250 | 253 | self.cur_diff_size += len(string) |
|
251 | 254 | |
|
252 | 255 | if not self.show_full_diff and (self.cur_diff_size > self.diff_limit): |
|
253 | 256 | raise DiffLimitExceeded('Diff Limit Exceeded') |
|
254 | 257 | |
|
255 | 258 | return safe_unicode(string)\ |
|
256 | 259 | .replace('&', '&')\ |
|
257 | 260 | .replace('<', '<')\ |
|
258 | 261 | .replace('>', '>') |
|
259 | 262 | |
|
260 | 263 | def _line_counter(self, l): |
|
261 | 264 | """ |
|
262 | 265 | Checks each line and bumps total adds/removes for this diff |
|
263 | 266 | |
|
264 | 267 | :param l: |
|
265 | 268 | """ |
|
266 | 269 | if l.startswith('+') and not l.startswith('+++'): |
|
267 | 270 | self.adds += 1 |
|
268 | 271 | elif l.startswith('-') and not l.startswith('---'): |
|
269 | 272 | self.removes += 1 |
|
270 | 273 | return safe_unicode(l) |
|
271 | 274 | |
|
272 | 275 | def _highlight_line_difflib(self, line, next_): |
|
273 | 276 | """ |
|
274 | 277 | Highlight inline changes in both lines. |
|
275 | 278 | """ |
|
276 | 279 | |
|
277 | 280 | if line['action'] == Action.DELETE: |
|
278 | 281 | old, new = line, next_ |
|
279 | 282 | else: |
|
280 | 283 | old, new = next_, line |
|
281 | 284 | |
|
282 | 285 | oldwords = self._token_re.split(old['line']) |
|
283 | 286 | newwords = self._token_re.split(new['line']) |
|
284 | 287 | sequence = difflib.SequenceMatcher(None, oldwords, newwords) |
|
285 | 288 | |
|
286 | 289 | oldfragments, newfragments = [], [] |
|
287 | 290 | for tag, i1, i2, j1, j2 in sequence.get_opcodes(): |
|
288 | 291 | oldfrag = ''.join(oldwords[i1:i2]) |
|
289 | 292 | newfrag = ''.join(newwords[j1:j2]) |
|
290 | 293 | if tag != 'equal': |
|
291 | 294 | if oldfrag: |
|
292 | 295 | oldfrag = '<del>%s</del>' % oldfrag |
|
293 | 296 | if newfrag: |
|
294 | 297 | newfrag = '<ins>%s</ins>' % newfrag |
|
295 | 298 | oldfragments.append(oldfrag) |
|
296 | 299 | newfragments.append(newfrag) |
|
297 | 300 | |
|
298 | 301 | old['line'] = "".join(oldfragments) |
|
299 | 302 | new['line'] = "".join(newfragments) |
|
300 | 303 | |
|
301 | 304 | def _highlight_line_udiff(self, line, next_): |
|
302 | 305 | """ |
|
303 | 306 | Highlight inline changes in both lines. |
|
304 | 307 | """ |
|
305 | 308 | start = 0 |
|
306 | 309 | limit = min(len(line['line']), len(next_['line'])) |
|
307 | 310 | while start < limit and line['line'][start] == next_['line'][start]: |
|
308 | 311 | start += 1 |
|
309 | 312 | end = -1 |
|
310 | 313 | limit -= start |
|
311 | 314 | while -end <= limit and line['line'][end] == next_['line'][end]: |
|
312 | 315 | end -= 1 |
|
313 | 316 | end += 1 |
|
314 | 317 | if start or end: |
|
315 | 318 | def do(l): |
|
316 | 319 | last = end + len(l['line']) |
|
317 | 320 | if l['action'] == Action.ADD: |
|
318 | 321 | tag = 'ins' |
|
319 | 322 | else: |
|
320 | 323 | tag = 'del' |
|
321 | 324 | l['line'] = '%s<%s>%s</%s>%s' % ( |
|
322 | 325 | l['line'][:start], |
|
323 | 326 | tag, |
|
324 | 327 | l['line'][start:last], |
|
325 | 328 | tag, |
|
326 | 329 | l['line'][last:] |
|
327 | 330 | ) |
|
328 | 331 | do(line) |
|
329 | 332 | do(next_) |
|
330 | 333 | |
|
331 | 334 | def _clean_line(self, line, command): |
|
332 | 335 | if command in ['+', '-', ' ']: |
|
333 | 336 | # only modify the line if it's actually a diff thing |
|
334 | 337 | line = line[1:] |
|
335 | 338 | return line |
|
336 | 339 | |
|
337 | 340 | def _parse_gitdiff(self, inline_diff=True): |
|
338 | 341 | _files = [] |
|
339 | 342 | diff_container = lambda arg: arg |
|
340 | 343 | |
|
341 | 344 | for chunk in self._diff.chunks(): |
|
342 | 345 | head = chunk.header |
|
343 | 346 | |
|
344 | 347 | diff = imap(self._escaper, chunk.diff.splitlines(1)) |
|
345 | 348 | raw_diff = chunk.raw |
|
346 | 349 | limited_diff = False |
|
347 | 350 | exceeds_limit = False |
|
348 | 351 | |
|
349 | 352 | op = None |
|
350 | 353 | stats = { |
|
351 | 354 | 'added': 0, |
|
352 | 355 | 'deleted': 0, |
|
353 | 356 | 'binary': False, |
|
354 | 357 | 'ops': {}, |
|
355 | 358 | } |
|
356 | 359 | |
|
357 | 360 | if head['deleted_file_mode']: |
|
358 | 361 | op = OPS.DEL |
|
359 | 362 | stats['binary'] = True |
|
360 | 363 | stats['ops'][DEL_FILENODE] = 'deleted file' |
|
361 | 364 | |
|
362 | 365 | elif head['new_file_mode']: |
|
363 | 366 | op = OPS.ADD |
|
364 | 367 | stats['binary'] = True |
|
365 | 368 | stats['ops'][NEW_FILENODE] = 'new file %s' % head['new_file_mode'] |
|
366 | 369 | else: # modify operation, can be copy, rename or chmod |
|
367 | 370 | |
|
368 | 371 | # CHMOD |
|
369 | 372 | if head['new_mode'] and head['old_mode']: |
|
370 | 373 | op = OPS.MOD |
|
371 | 374 | stats['binary'] = True |
|
372 | 375 | stats['ops'][CHMOD_FILENODE] = ( |
|
373 | 376 | 'modified file chmod %s => %s' % ( |
|
374 | 377 | head['old_mode'], head['new_mode'])) |
|
375 | 378 | # RENAME |
|
376 | 379 | if head['rename_from'] != head['rename_to']: |
|
377 | 380 | op = OPS.MOD |
|
378 | 381 | stats['binary'] = True |
|
379 | 382 | stats['ops'][RENAMED_FILENODE] = ( |
|
380 | 383 | 'file renamed from %s to %s' % ( |
|
381 | 384 | head['rename_from'], head['rename_to'])) |
|
382 | 385 | # COPY |
|
383 | 386 | if head.get('copy_from') and head.get('copy_to'): |
|
384 | 387 | op = OPS.MOD |
|
385 | 388 | stats['binary'] = True |
|
386 | 389 | stats['ops'][COPIED_FILENODE] = ( |
|
387 | 390 | 'file copied from %s to %s' % ( |
|
388 | 391 | head['copy_from'], head['copy_to'])) |
|
389 | 392 | |
|
390 | 393 | # If our new parsed headers didn't match anything fallback to |
|
391 | 394 | # old style detection |
|
392 | 395 | if op is None: |
|
393 | 396 | if not head['a_file'] and head['b_file']: |
|
394 | 397 | op = OPS.ADD |
|
395 | 398 | stats['binary'] = True |
|
396 | 399 | stats['ops'][NEW_FILENODE] = 'new file' |
|
397 | 400 | |
|
398 | 401 | elif head['a_file'] and not head['b_file']: |
|
399 | 402 | op = OPS.DEL |
|
400 | 403 | stats['binary'] = True |
|
401 | 404 | stats['ops'][DEL_FILENODE] = 'deleted file' |
|
402 | 405 | |
|
403 | 406 | # it's not ADD not DELETE |
|
404 | 407 | if op is None: |
|
405 | 408 | op = OPS.MOD |
|
406 | 409 | stats['binary'] = True |
|
407 | 410 | stats['ops'][MOD_FILENODE] = 'modified file' |
|
408 | 411 | |
|
409 | 412 | # a real non-binary diff |
|
410 | 413 | if head['a_file'] or head['b_file']: |
|
411 | 414 | try: |
|
412 | 415 | raw_diff, chunks, _stats = self._parse_lines(diff) |
|
413 | 416 | stats['binary'] = False |
|
414 | 417 | stats['added'] = _stats[0] |
|
415 | 418 | stats['deleted'] = _stats[1] |
|
416 | 419 | # explicit mark that it's a modified file |
|
417 | 420 | if op == OPS.MOD: |
|
418 | 421 | stats['ops'][MOD_FILENODE] = 'modified file' |
|
419 | 422 | exceeds_limit = len(raw_diff) > self.file_limit |
|
420 | 423 | |
|
421 | 424 | # changed from _escaper function so we validate size of |
|
422 | 425 | # each file instead of the whole diff |
|
423 | 426 | # diff will hide big files but still show small ones |
|
424 | 427 | # from my tests, big files are fairly safe to be parsed |
|
425 | 428 | # but the browser is the bottleneck |
|
426 | 429 | if not self.show_full_diff and exceeds_limit: |
|
427 | 430 | raise DiffLimitExceeded('File Limit Exceeded') |
|
428 | 431 | |
|
429 | 432 | except DiffLimitExceeded: |
|
430 | 433 | diff_container = lambda _diff: \ |
|
431 | 434 | LimitedDiffContainer( |
|
432 | 435 | self.diff_limit, self.cur_diff_size, _diff) |
|
433 | 436 | |
|
434 | 437 | exceeds_limit = len(raw_diff) > self.file_limit |
|
435 | 438 | limited_diff = True |
|
436 | 439 | chunks = [] |
|
437 | 440 | |
|
438 | 441 | else: # GIT format binary patch, or possibly empty diff |
|
439 | 442 | if head['bin_patch']: |
|
440 | 443 | # we have operation already extracted, but we mark simply |
|
441 | 444 | # it's a diff we wont show for binary files |
|
442 | 445 | stats['ops'][BIN_FILENODE] = 'binary diff hidden' |
|
443 | 446 | chunks = [] |
|
444 | 447 | |
|
445 | 448 | if chunks and not self.show_full_diff and op == OPS.DEL: |
|
446 | 449 | # if not full diff mode show deleted file contents |
|
447 | 450 | # TODO: anderson: if the view is not too big, there is no way |
|
448 | 451 | # to see the content of the file |
|
449 | 452 | chunks = [] |
|
450 | 453 | |
|
451 | 454 | chunks.insert(0, [{ |
|
452 | 455 | 'old_lineno': '', |
|
453 | 456 | 'new_lineno': '', |
|
454 | 457 | 'action': Action.CONTEXT, |
|
455 | 458 | 'line': msg, |
|
456 | 459 | } for _op, msg in stats['ops'].iteritems() |
|
457 | 460 | if _op not in [MOD_FILENODE]]) |
|
458 | 461 | |
|
459 | 462 | _files.append({ |
|
460 | 463 | 'filename': safe_unicode(head['b_path']), |
|
461 | 464 | 'old_revision': head['a_blob_id'], |
|
462 | 465 | 'new_revision': head['b_blob_id'], |
|
463 | 466 | 'chunks': chunks, |
|
464 | 467 | 'raw_diff': safe_unicode(raw_diff), |
|
465 | 468 | 'operation': op, |
|
466 | 469 | 'stats': stats, |
|
467 | 470 | 'exceeds_limit': exceeds_limit, |
|
468 | 471 | 'is_limited_diff': limited_diff, |
|
469 | 472 | }) |
|
470 | 473 | |
|
471 | 474 | sorter = lambda info: {OPS.ADD: 0, OPS.MOD: 1, |
|
472 | 475 | OPS.DEL: 2}.get(info['operation']) |
|
473 | 476 | |
|
474 | 477 | if not inline_diff: |
|
475 | 478 | return diff_container(sorted(_files, key=sorter)) |
|
476 | 479 | |
|
477 | 480 | # highlight inline changes |
|
478 | 481 | for diff_data in _files: |
|
479 | 482 | for chunk in diff_data['chunks']: |
|
480 | 483 | lineiter = iter(chunk) |
|
481 | 484 | try: |
|
482 | 485 | while 1: |
|
483 | 486 | line = lineiter.next() |
|
484 | 487 | if line['action'] not in ( |
|
485 | 488 | Action.UNMODIFIED, Action.CONTEXT): |
|
486 | 489 | nextline = lineiter.next() |
|
487 | 490 | if nextline['action'] in ['unmod', 'context'] or \ |
|
488 | 491 | nextline['action'] == line['action']: |
|
489 | 492 | continue |
|
490 | 493 | self.differ(line, nextline) |
|
491 | 494 | except StopIteration: |
|
492 | 495 | pass |
|
493 | 496 | |
|
494 | 497 | return diff_container(sorted(_files, key=sorter)) |
|
495 | 498 | |
|
496 | 499 | |
|
497 | 500 | # FIXME: NEWDIFFS: dan: this replaces the old _escaper function |
|
498 | 501 | def _process_line(self, string): |
|
499 | 502 | """ |
|
500 | 503 | Process a diff line, checks the diff limit |
|
501 | 504 | |
|
502 | 505 | :param string: |
|
503 | 506 | """ |
|
504 | 507 | |
|
505 | 508 | self.cur_diff_size += len(string) |
|
506 | 509 | |
|
507 | 510 | if not self.show_full_diff and (self.cur_diff_size > self.diff_limit): |
|
508 | 511 | raise DiffLimitExceeded('Diff Limit Exceeded') |
|
509 | 512 | |
|
510 | 513 | return safe_unicode(string) |
|
511 | 514 | |
|
512 | 515 | # FIXME: NEWDIFFS: dan: this replaces _parse_gitdiff |
|
513 | 516 | def _new_parse_gitdiff(self, inline_diff=True): |
|
514 | 517 | _files = [] |
|
515 | 518 | diff_container = lambda arg: arg |
|
516 | 519 | for chunk in self._diff.chunks(): |
|
517 | 520 | head = chunk.header |
|
518 | 521 | log.debug('parsing diff %r' % head) |
|
519 | 522 | |
|
520 | 523 | diff = imap(self._process_line, chunk.diff.splitlines(1)) |
|
521 | 524 | raw_diff = chunk.raw |
|
522 | 525 | limited_diff = False |
|
523 | 526 | exceeds_limit = False |
|
524 | 527 | # if 'empty_file_to_modify_and_rename' in head['a_path']: |
|
525 | 528 | # 1/0 |
|
526 | 529 | op = None |
|
527 | 530 | stats = { |
|
528 | 531 | 'added': 0, |
|
529 | 532 | 'deleted': 0, |
|
530 | 533 | 'binary': False, |
|
531 | 534 | 'old_mode': None, |
|
532 | 535 | 'new_mode': None, |
|
533 | 536 | 'ops': {}, |
|
534 | 537 | } |
|
535 | 538 | if head['old_mode']: |
|
536 | 539 | stats['old_mode'] = head['old_mode'] |
|
537 | 540 | if head['new_mode']: |
|
538 | 541 | stats['new_mode'] = head['new_mode'] |
|
539 | 542 | if head['b_mode']: |
|
540 | 543 | stats['new_mode'] = head['b_mode'] |
|
541 | 544 | |
|
542 | 545 | if head['deleted_file_mode']: |
|
543 | 546 | op = OPS.DEL |
|
544 | 547 | stats['binary'] = True |
|
545 | 548 | stats['ops'][DEL_FILENODE] = 'deleted file' |
|
546 | 549 | |
|
547 | 550 | elif head['new_file_mode']: |
|
548 | 551 | op = OPS.ADD |
|
549 | 552 | stats['binary'] = True |
|
550 | 553 | stats['old_mode'] = None |
|
551 | 554 | stats['new_mode'] = head['new_file_mode'] |
|
552 | 555 | stats['ops'][NEW_FILENODE] = 'new file %s' % head['new_file_mode'] |
|
553 | 556 | else: # modify operation, can be copy, rename or chmod |
|
554 | 557 | |
|
555 | 558 | # CHMOD |
|
556 | 559 | if head['new_mode'] and head['old_mode']: |
|
557 | 560 | op = OPS.MOD |
|
558 | 561 | stats['binary'] = True |
|
559 | 562 | stats['ops'][CHMOD_FILENODE] = ( |
|
560 | 563 | 'modified file chmod %s => %s' % ( |
|
561 | 564 | head['old_mode'], head['new_mode'])) |
|
562 | 565 | |
|
563 | 566 | # RENAME |
|
564 | 567 | if head['rename_from'] != head['rename_to']: |
|
565 | 568 | op = OPS.MOD |
|
566 | 569 | stats['binary'] = True |
|
567 | 570 | stats['renamed'] = (head['rename_from'], head['rename_to']) |
|
568 | 571 | stats['ops'][RENAMED_FILENODE] = ( |
|
569 | 572 | 'file renamed from %s to %s' % ( |
|
570 | 573 | head['rename_from'], head['rename_to'])) |
|
571 | 574 | # COPY |
|
572 | 575 | if head.get('copy_from') and head.get('copy_to'): |
|
573 | 576 | op = OPS.MOD |
|
574 | 577 | stats['binary'] = True |
|
575 | 578 | stats['copied'] = (head['copy_from'], head['copy_to']) |
|
576 | 579 | stats['ops'][COPIED_FILENODE] = ( |
|
577 | 580 | 'file copied from %s to %s' % ( |
|
578 | 581 | head['copy_from'], head['copy_to'])) |
|
579 | 582 | |
|
580 | 583 | # If our new parsed headers didn't match anything fallback to |
|
581 | 584 | # old style detection |
|
582 | 585 | if op is None: |
|
583 | 586 | if not head['a_file'] and head['b_file']: |
|
584 | 587 | op = OPS.ADD |
|
585 | 588 | stats['binary'] = True |
|
586 | 589 | stats['new_file'] = True |
|
587 | 590 | stats['ops'][NEW_FILENODE] = 'new file' |
|
588 | 591 | |
|
589 | 592 | elif head['a_file'] and not head['b_file']: |
|
590 | 593 | op = OPS.DEL |
|
591 | 594 | stats['binary'] = True |
|
592 | 595 | stats['ops'][DEL_FILENODE] = 'deleted file' |
|
593 | 596 | |
|
594 | 597 | # it's not ADD not DELETE |
|
595 | 598 | if op is None: |
|
596 | 599 | op = OPS.MOD |
|
597 | 600 | stats['binary'] = True |
|
598 | 601 | stats['ops'][MOD_FILENODE] = 'modified file' |
|
599 | 602 | |
|
600 | 603 | # a real non-binary diff |
|
601 | 604 | if head['a_file'] or head['b_file']: |
|
602 | 605 | try: |
|
603 | 606 | raw_diff, chunks, _stats = self._new_parse_lines(diff) |
|
604 | 607 | stats['binary'] = False |
|
605 | 608 | stats['added'] = _stats[0] |
|
606 | 609 | stats['deleted'] = _stats[1] |
|
607 | 610 | # explicit mark that it's a modified file |
|
608 | 611 | if op == OPS.MOD: |
|
609 | 612 | stats['ops'][MOD_FILENODE] = 'modified file' |
|
610 | 613 | exceeds_limit = len(raw_diff) > self.file_limit |
|
611 | 614 | |
|
612 | 615 | # changed from _escaper function so we validate size of |
|
613 | 616 | # each file instead of the whole diff |
|
614 | 617 | # diff will hide big files but still show small ones |
|
615 | 618 | # from my tests, big files are fairly safe to be parsed |
|
616 | 619 | # but the browser is the bottleneck |
|
617 | 620 | if not self.show_full_diff and exceeds_limit: |
|
618 | 621 | raise DiffLimitExceeded('File Limit Exceeded') |
|
619 | 622 | |
|
620 | 623 | except DiffLimitExceeded: |
|
621 | 624 | diff_container = lambda _diff: \ |
|
622 | 625 | LimitedDiffContainer( |
|
623 | 626 | self.diff_limit, self.cur_diff_size, _diff) |
|
624 | 627 | |
|
625 | 628 | exceeds_limit = len(raw_diff) > self.file_limit |
|
626 | 629 | limited_diff = True |
|
627 | 630 | chunks = [] |
|
628 | 631 | |
|
629 | 632 | else: # GIT format binary patch, or possibly empty diff |
|
630 | 633 | if head['bin_patch']: |
|
631 | 634 | # we have operation already extracted, but we mark simply |
|
632 | 635 | # it's a diff we wont show for binary files |
|
633 | 636 | stats['ops'][BIN_FILENODE] = 'binary diff hidden' |
|
634 | 637 | chunks = [] |
|
635 | 638 | |
|
636 | 639 | if chunks and not self.show_full_diff and op == OPS.DEL: |
|
637 | 640 | # if not full diff mode show deleted file contents |
|
638 | 641 | # TODO: anderson: if the view is not too big, there is no way |
|
639 | 642 | # to see the content of the file |
|
640 | 643 | chunks = [] |
|
641 | 644 | |
|
642 | 645 | chunks.insert(0, [{ |
|
643 | 646 | 'old_lineno': '', |
|
644 | 647 | 'new_lineno': '', |
|
645 | 648 | 'action': Action.CONTEXT, |
|
646 | 649 | 'line': msg, |
|
647 | 650 | } for _op, msg in stats['ops'].iteritems() |
|
648 | 651 | if _op not in [MOD_FILENODE]]) |
|
649 | 652 | |
|
650 | 653 | original_filename = safe_unicode(head['a_path']) |
|
651 | 654 | _files.append({ |
|
652 | 655 | 'original_filename': original_filename, |
|
653 | 656 | 'filename': safe_unicode(head['b_path']), |
|
654 | 657 | 'old_revision': head['a_blob_id'], |
|
655 | 658 | 'new_revision': head['b_blob_id'], |
|
656 | 659 | 'chunks': chunks, |
|
657 | 660 | 'raw_diff': safe_unicode(raw_diff), |
|
658 | 661 | 'operation': op, |
|
659 | 662 | 'stats': stats, |
|
660 | 663 | 'exceeds_limit': exceeds_limit, |
|
661 | 664 | 'is_limited_diff': limited_diff, |
|
662 | 665 | }) |
|
663 | 666 | |
|
664 | 667 | |
|
665 | 668 | sorter = lambda info: {OPS.ADD: 0, OPS.MOD: 1, |
|
666 | 669 | OPS.DEL: 2}.get(info['operation']) |
|
667 | 670 | |
|
668 | 671 | return diff_container(sorted(_files, key=sorter)) |
|
669 | 672 | |
|
670 | 673 | # FIXME: NEWDIFFS: dan: this gets replaced by _new_parse_lines |
|
671 | 674 | def _parse_lines(self, diff): |
|
672 | 675 | """ |
|
673 | 676 | Parse the diff an return data for the template. |
|
674 | 677 | """ |
|
675 | 678 | |
|
676 | 679 | lineiter = iter(diff) |
|
677 | 680 | stats = [0, 0] |
|
678 | 681 | chunks = [] |
|
679 | 682 | raw_diff = [] |
|
680 | 683 | |
|
681 | 684 | try: |
|
682 | 685 | line = lineiter.next() |
|
683 | 686 | |
|
684 | 687 | while line: |
|
685 | 688 | raw_diff.append(line) |
|
686 | 689 | lines = [] |
|
687 | 690 | chunks.append(lines) |
|
688 | 691 | |
|
689 | 692 | match = self._chunk_re.match(line) |
|
690 | 693 | |
|
691 | 694 | if not match: |
|
692 | 695 | break |
|
693 | 696 | |
|
694 | 697 | gr = match.groups() |
|
695 | 698 | (old_line, old_end, |
|
696 | 699 | new_line, new_end) = [int(x or 1) for x in gr[:-1]] |
|
697 | 700 | old_line -= 1 |
|
698 | 701 | new_line -= 1 |
|
699 | 702 | |
|
700 | 703 | context = len(gr) == 5 |
|
701 | 704 | old_end += old_line |
|
702 | 705 | new_end += new_line |
|
703 | 706 | |
|
704 | 707 | if context: |
|
705 | 708 | # skip context only if it's first line |
|
706 | 709 | if int(gr[0]) > 1: |
|
707 | 710 | lines.append({ |
|
708 | 711 | 'old_lineno': '...', |
|
709 | 712 | 'new_lineno': '...', |
|
710 | 713 | 'action': Action.CONTEXT, |
|
711 | 714 | 'line': line, |
|
712 | 715 | }) |
|
713 | 716 | |
|
714 | 717 | line = lineiter.next() |
|
715 | 718 | |
|
716 | 719 | while old_line < old_end or new_line < new_end: |
|
717 | 720 | command = ' ' |
|
718 | 721 | if line: |
|
719 | 722 | command = line[0] |
|
720 | 723 | |
|
721 | 724 | affects_old = affects_new = False |
|
722 | 725 | |
|
723 | 726 | # ignore those if we don't expect them |
|
724 | 727 | if command in '#@': |
|
725 | 728 | continue |
|
726 | 729 | elif command == '+': |
|
727 | 730 | affects_new = True |
|
728 | 731 | action = Action.ADD |
|
729 | 732 | stats[0] += 1 |
|
730 | 733 | elif command == '-': |
|
731 | 734 | affects_old = True |
|
732 | 735 | action = Action.DELETE |
|
733 | 736 | stats[1] += 1 |
|
734 | 737 | else: |
|
735 | 738 | affects_old = affects_new = True |
|
736 | 739 | action = Action.UNMODIFIED |
|
737 | 740 | |
|
738 | 741 | if not self._newline_marker.match(line): |
|
739 | 742 | old_line += affects_old |
|
740 | 743 | new_line += affects_new |
|
741 | 744 | lines.append({ |
|
742 | 745 | 'old_lineno': affects_old and old_line or '', |
|
743 | 746 | 'new_lineno': affects_new and new_line or '', |
|
744 | 747 | 'action': action, |
|
745 | 748 | 'line': self._clean_line(line, command) |
|
746 | 749 | }) |
|
747 | 750 | raw_diff.append(line) |
|
748 | 751 | |
|
749 | 752 | line = lineiter.next() |
|
750 | 753 | |
|
751 | 754 | if self._newline_marker.match(line): |
|
752 | 755 | # we need to append to lines, since this is not |
|
753 | 756 | # counted in the line specs of diff |
|
754 | 757 | lines.append({ |
|
755 | 758 | 'old_lineno': '...', |
|
756 | 759 | 'new_lineno': '...', |
|
757 | 760 | 'action': Action.CONTEXT, |
|
758 | 761 | 'line': self._clean_line(line, command) |
|
759 | 762 | }) |
|
760 | 763 | |
|
761 | 764 | except StopIteration: |
|
762 | 765 | pass |
|
763 | 766 | return ''.join(raw_diff), chunks, stats |
|
764 | 767 | |
|
765 | 768 | # FIXME: NEWDIFFS: dan: this replaces _parse_lines |
|
766 | 769 | def _new_parse_lines(self, diff): |
|
767 | 770 | """ |
|
768 | 771 | Parse the diff an return data for the template. |
|
769 | 772 | """ |
|
770 | 773 | |
|
771 | 774 | lineiter = iter(diff) |
|
772 | 775 | stats = [0, 0] |
|
773 | 776 | chunks = [] |
|
774 | 777 | raw_diff = [] |
|
775 | 778 | |
|
776 | 779 | try: |
|
777 | 780 | line = lineiter.next() |
|
778 | 781 | |
|
779 | 782 | while line: |
|
780 | 783 | raw_diff.append(line) |
|
781 | 784 | match = self._chunk_re.match(line) |
|
782 | 785 | |
|
783 | 786 | if not match: |
|
784 | 787 | break |
|
785 | 788 | |
|
786 | 789 | gr = match.groups() |
|
787 | 790 | (old_line, old_end, |
|
788 | 791 | new_line, new_end) = [int(x or 1) for x in gr[:-1]] |
|
789 | 792 | |
|
790 | 793 | lines = [] |
|
791 | 794 | hunk = { |
|
792 | 795 | 'section_header': gr[-1], |
|
793 | 796 | 'source_start': old_line, |
|
794 | 797 | 'source_length': old_end, |
|
795 | 798 | 'target_start': new_line, |
|
796 | 799 | 'target_length': new_end, |
|
797 | 800 | 'lines': lines, |
|
798 | 801 | } |
|
799 | 802 | chunks.append(hunk) |
|
800 | 803 | |
|
801 | 804 | old_line -= 1 |
|
802 | 805 | new_line -= 1 |
|
803 | 806 | |
|
804 | 807 | context = len(gr) == 5 |
|
805 | 808 | old_end += old_line |
|
806 | 809 | new_end += new_line |
|
807 | 810 | |
|
808 | 811 | line = lineiter.next() |
|
809 | 812 | |
|
810 | 813 | while old_line < old_end or new_line < new_end: |
|
811 | 814 | command = ' ' |
|
812 | 815 | if line: |
|
813 | 816 | command = line[0] |
|
814 | 817 | |
|
815 | 818 | affects_old = affects_new = False |
|
816 | 819 | |
|
817 | 820 | # ignore those if we don't expect them |
|
818 | 821 | if command in '#@': |
|
819 | 822 | continue |
|
820 | 823 | elif command == '+': |
|
821 | 824 | affects_new = True |
|
822 | 825 | action = Action.ADD |
|
823 | 826 | stats[0] += 1 |
|
824 | 827 | elif command == '-': |
|
825 | 828 | affects_old = True |
|
826 | 829 | action = Action.DELETE |
|
827 | 830 | stats[1] += 1 |
|
828 | 831 | else: |
|
829 | 832 | affects_old = affects_new = True |
|
830 | 833 | action = Action.UNMODIFIED |
|
831 | 834 | |
|
832 | 835 | if not self._newline_marker.match(line): |
|
833 | 836 | old_line += affects_old |
|
834 | 837 | new_line += affects_new |
|
835 | 838 | lines.append({ |
|
836 | 839 | 'old_lineno': affects_old and old_line or '', |
|
837 | 840 | 'new_lineno': affects_new and new_line or '', |
|
838 | 841 | 'action': action, |
|
839 | 842 | 'line': self._clean_line(line, command) |
|
840 | 843 | }) |
|
841 | 844 | raw_diff.append(line) |
|
842 | 845 | |
|
843 | 846 | line = lineiter.next() |
|
844 | 847 | |
|
845 | 848 | if self._newline_marker.match(line): |
|
846 | 849 | # we need to append to lines, since this is not |
|
847 | 850 | # counted in the line specs of diff |
|
848 | 851 | if affects_old: |
|
849 | 852 | action = Action.OLD_NO_NL |
|
850 | 853 | elif affects_new: |
|
851 | 854 | action = Action.NEW_NO_NL |
|
852 | 855 | else: |
|
853 | 856 | raise Exception('invalid context for no newline') |
|
854 | 857 | |
|
855 | 858 | lines.append({ |
|
856 | 859 | 'old_lineno': None, |
|
857 | 860 | 'new_lineno': None, |
|
858 | 861 | 'action': action, |
|
859 | 862 | 'line': self._clean_line(line, command) |
|
860 | 863 | }) |
|
861 | 864 | |
|
862 | 865 | except StopIteration: |
|
863 | 866 | pass |
|
864 | 867 | return ''.join(raw_diff), chunks, stats |
|
865 | 868 | |
|
866 | 869 | def _safe_id(self, idstring): |
|
867 | 870 | """Make a string safe for including in an id attribute. |
|
868 | 871 | |
|
869 | 872 | The HTML spec says that id attributes 'must begin with |
|
870 | 873 | a letter ([A-Za-z]) and may be followed by any number |
|
871 | 874 | of letters, digits ([0-9]), hyphens ("-"), underscores |
|
872 | 875 | ("_"), colons (":"), and periods (".")'. These regexps |
|
873 | 876 | are slightly over-zealous, in that they remove colons |
|
874 | 877 | and periods unnecessarily. |
|
875 | 878 | |
|
876 | 879 | Whitespace is transformed into underscores, and then |
|
877 | 880 | anything which is not a hyphen or a character that |
|
878 | 881 | matches \w (alphanumerics and underscore) is removed. |
|
879 | 882 | |
|
880 | 883 | """ |
|
881 | 884 | # Transform all whitespace to underscore |
|
882 | 885 | idstring = re.sub(r'\s', "_", '%s' % idstring) |
|
883 | 886 | # Remove everything that is not a hyphen or a member of \w |
|
884 | 887 | idstring = re.sub(r'(?!-)\W', "", idstring).lower() |
|
885 | 888 | return idstring |
|
886 | 889 | |
|
887 | 890 | def prepare(self, inline_diff=True): |
|
888 | 891 | """ |
|
889 | 892 | Prepare the passed udiff for HTML rendering. |
|
890 | 893 | |
|
891 | 894 | :return: A list of dicts with diff information. |
|
892 | 895 | """ |
|
893 | 896 | parsed = self._parser(inline_diff=inline_diff) |
|
894 | 897 | self.parsed = True |
|
895 | 898 | self.parsed_diff = parsed |
|
896 | 899 | return parsed |
|
897 | 900 | |
|
898 | 901 | def as_raw(self, diff_lines=None): |
|
899 | 902 | """ |
|
900 | 903 | Returns raw diff as a byte string |
|
901 | 904 | """ |
|
902 | 905 | return self._diff.raw |
|
903 | 906 | |
|
904 | 907 | def as_html(self, table_class='code-difftable', line_class='line', |
|
905 | 908 | old_lineno_class='lineno old', new_lineno_class='lineno new', |
|
906 | 909 | code_class='code', enable_comments=False, parsed_lines=None): |
|
907 | 910 | """ |
|
908 | 911 | Return given diff as html table with customized css classes |
|
909 | 912 | """ |
|
910 | 913 | def _link_to_if(condition, label, url): |
|
911 | 914 | """ |
|
912 | 915 | Generates a link if condition is meet or just the label if not. |
|
913 | 916 | """ |
|
914 | 917 | |
|
915 | 918 | if condition: |
|
916 | 919 | return '''<a href="%(url)s" class="tooltip" |
|
917 | 920 | title="%(title)s">%(label)s</a>''' % { |
|
918 | 921 | 'title': _('Click to select line'), |
|
919 | 922 | 'url': url, |
|
920 | 923 | 'label': label |
|
921 | 924 | } |
|
922 | 925 | else: |
|
923 | 926 | return label |
|
924 | 927 | if not self.parsed: |
|
925 | 928 | self.prepare() |
|
926 | 929 | |
|
927 | 930 | diff_lines = self.parsed_diff |
|
928 | 931 | if parsed_lines: |
|
929 | 932 | diff_lines = parsed_lines |
|
930 | 933 | |
|
931 | 934 | _html_empty = True |
|
932 | 935 | _html = [] |
|
933 | 936 | _html.append('''<table class="%(table_class)s">\n''' % { |
|
934 | 937 | 'table_class': table_class |
|
935 | 938 | }) |
|
936 | 939 | |
|
937 | 940 | for diff in diff_lines: |
|
938 | 941 | for line in diff['chunks']: |
|
939 | 942 | _html_empty = False |
|
940 | 943 | for change in line: |
|
941 | 944 | _html.append('''<tr class="%(lc)s %(action)s">\n''' % { |
|
942 | 945 | 'lc': line_class, |
|
943 | 946 | 'action': change['action'] |
|
944 | 947 | }) |
|
945 | 948 | anchor_old_id = '' |
|
946 | 949 | anchor_new_id = '' |
|
947 | 950 | anchor_old = "%(filename)s_o%(oldline_no)s" % { |
|
948 | 951 | 'filename': self._safe_id(diff['filename']), |
|
949 | 952 | 'oldline_no': change['old_lineno'] |
|
950 | 953 | } |
|
951 | 954 | anchor_new = "%(filename)s_n%(oldline_no)s" % { |
|
952 | 955 | 'filename': self._safe_id(diff['filename']), |
|
953 | 956 | 'oldline_no': change['new_lineno'] |
|
954 | 957 | } |
|
955 | 958 | cond_old = (change['old_lineno'] != '...' and |
|
956 | 959 | change['old_lineno']) |
|
957 | 960 | cond_new = (change['new_lineno'] != '...' and |
|
958 | 961 | change['new_lineno']) |
|
959 | 962 | if cond_old: |
|
960 | 963 | anchor_old_id = 'id="%s"' % anchor_old |
|
961 | 964 | if cond_new: |
|
962 | 965 | anchor_new_id = 'id="%s"' % anchor_new |
|
963 | 966 | |
|
964 | 967 | if change['action'] != Action.CONTEXT: |
|
965 | 968 | anchor_link = True |
|
966 | 969 | else: |
|
967 | 970 | anchor_link = False |
|
968 | 971 | |
|
969 | 972 | ########################################################### |
|
970 | 973 | # COMMENT ICONS |
|
971 | 974 | ########################################################### |
|
972 | 975 | _html.append('''\t<td class="add-comment-line"><span class="add-comment-content">''') |
|
973 | 976 | |
|
974 | 977 | if enable_comments and change['action'] != Action.CONTEXT: |
|
975 | 978 | _html.append('''<a href="#"><span class="icon-comment-add"></span></a>''') |
|
976 | 979 | |
|
977 | 980 | _html.append('''</span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>\n''') |
|
978 | 981 | |
|
979 | 982 | ########################################################### |
|
980 | 983 | # OLD LINE NUMBER |
|
981 | 984 | ########################################################### |
|
982 | 985 | _html.append('''\t<td %(a_id)s class="%(olc)s">''' % { |
|
983 | 986 | 'a_id': anchor_old_id, |
|
984 | 987 | 'olc': old_lineno_class |
|
985 | 988 | }) |
|
986 | 989 | |
|
987 | 990 | _html.append('''%(link)s''' % { |
|
988 | 991 | 'link': _link_to_if(anchor_link, change['old_lineno'], |
|
989 | 992 | '#%s' % anchor_old) |
|
990 | 993 | }) |
|
991 | 994 | _html.append('''</td>\n''') |
|
992 | 995 | ########################################################### |
|
993 | 996 | # NEW LINE NUMBER |
|
994 | 997 | ########################################################### |
|
995 | 998 | |
|
996 | 999 | _html.append('''\t<td %(a_id)s class="%(nlc)s">''' % { |
|
997 | 1000 | 'a_id': anchor_new_id, |
|
998 | 1001 | 'nlc': new_lineno_class |
|
999 | 1002 | }) |
|
1000 | 1003 | |
|
1001 | 1004 | _html.append('''%(link)s''' % { |
|
1002 | 1005 | 'link': _link_to_if(anchor_link, change['new_lineno'], |
|
1003 | 1006 | '#%s' % anchor_new) |
|
1004 | 1007 | }) |
|
1005 | 1008 | _html.append('''</td>\n''') |
|
1006 | 1009 | ########################################################### |
|
1007 | 1010 | # CODE |
|
1008 | 1011 | ########################################################### |
|
1009 | 1012 | code_classes = [code_class] |
|
1010 | 1013 | if (not enable_comments or |
|
1011 | 1014 | change['action'] == Action.CONTEXT): |
|
1012 | 1015 | code_classes.append('no-comment') |
|
1013 | 1016 | _html.append('\t<td class="%s">' % ' '.join(code_classes)) |
|
1014 | 1017 | _html.append('''\n\t\t<pre>%(code)s</pre>\n''' % { |
|
1015 | 1018 | 'code': change['line'] |
|
1016 | 1019 | }) |
|
1017 | 1020 | |
|
1018 | 1021 | _html.append('''\t</td>''') |
|
1019 | 1022 | _html.append('''\n</tr>\n''') |
|
1020 | 1023 | _html.append('''</table>''') |
|
1021 | 1024 | if _html_empty: |
|
1022 | 1025 | return None |
|
1023 | 1026 | return ''.join(_html) |
|
1024 | 1027 | |
|
1025 | 1028 | def stat(self): |
|
1026 | 1029 | """ |
|
1027 | 1030 | Returns tuple of added, and removed lines for this instance |
|
1028 | 1031 | """ |
|
1029 | 1032 | return self.adds, self.removes |
|
1030 | 1033 | |
|
1031 | 1034 | def get_context_of_line( |
|
1032 | 1035 | self, path, diff_line=None, context_before=3, context_after=3): |
|
1033 | 1036 | """ |
|
1034 | 1037 | Returns the context lines for the specified diff line. |
|
1035 | 1038 | |
|
1036 | 1039 | :type diff_line: :class:`DiffLineNumber` |
|
1037 | 1040 | """ |
|
1038 | 1041 | assert self.parsed, "DiffProcessor is not initialized." |
|
1039 | 1042 | |
|
1040 | 1043 | if None not in diff_line: |
|
1041 | 1044 | raise ValueError( |
|
1042 | 1045 | "Cannot specify both line numbers: {}".format(diff_line)) |
|
1043 | 1046 | |
|
1044 | 1047 | file_diff = self._get_file_diff(path) |
|
1045 | 1048 | chunk, idx = self._find_chunk_line_index(file_diff, diff_line) |
|
1046 | 1049 | |
|
1047 | 1050 | first_line_to_include = max(idx - context_before, 0) |
|
1048 | 1051 | first_line_after_context = idx + context_after + 1 |
|
1049 | 1052 | context_lines = chunk[first_line_to_include:first_line_after_context] |
|
1050 | 1053 | |
|
1051 | 1054 | line_contents = [ |
|
1052 | 1055 | _context_line(line) for line in context_lines |
|
1053 | 1056 | if _is_diff_content(line)] |
|
1054 | 1057 | # TODO: johbo: Interim fixup, the diff chunks drop the final newline. |
|
1055 | 1058 | # Once they are fixed, we can drop this line here. |
|
1056 | 1059 | if line_contents: |
|
1057 | 1060 | line_contents[-1] = ( |
|
1058 | 1061 | line_contents[-1][0], line_contents[-1][1].rstrip('\n') + '\n') |
|
1059 | 1062 | return line_contents |
|
1060 | 1063 | |
|
1061 | 1064 | def find_context(self, path, context, offset=0): |
|
1062 | 1065 | """ |
|
1063 | 1066 | Finds the given `context` inside of the diff. |
|
1064 | 1067 | |
|
1065 | 1068 | Use the parameter `offset` to specify which offset the target line has |
|
1066 | 1069 | inside of the given `context`. This way the correct diff line will be |
|
1067 | 1070 | returned. |
|
1068 | 1071 | |
|
1069 | 1072 | :param offset: Shall be used to specify the offset of the main line |
|
1070 | 1073 | within the given `context`. |
|
1071 | 1074 | """ |
|
1072 | 1075 | if offset < 0 or offset >= len(context): |
|
1073 | 1076 | raise ValueError( |
|
1074 | 1077 | "Only positive values up to the length of the context " |
|
1075 | 1078 | "minus one are allowed.") |
|
1076 | 1079 | |
|
1077 | 1080 | matches = [] |
|
1078 | 1081 | file_diff = self._get_file_diff(path) |
|
1079 | 1082 | |
|
1080 | 1083 | for chunk in file_diff['chunks']: |
|
1081 | 1084 | context_iter = iter(context) |
|
1082 | 1085 | for line_idx, line in enumerate(chunk): |
|
1083 | 1086 | try: |
|
1084 | 1087 | if _context_line(line) == context_iter.next(): |
|
1085 | 1088 | continue |
|
1086 | 1089 | except StopIteration: |
|
1087 | 1090 | matches.append((line_idx, chunk)) |
|
1088 | 1091 | context_iter = iter(context) |
|
1089 | 1092 | |
|
1090 | 1093 | # Increment position and triger StopIteration |
|
1091 | 1094 | # if we had a match at the end |
|
1092 | 1095 | line_idx += 1 |
|
1093 | 1096 | try: |
|
1094 | 1097 | context_iter.next() |
|
1095 | 1098 | except StopIteration: |
|
1096 | 1099 | matches.append((line_idx, chunk)) |
|
1097 | 1100 | |
|
1098 | 1101 | effective_offset = len(context) - offset |
|
1099 | 1102 | found_at_diff_lines = [ |
|
1100 | 1103 | _line_to_diff_line_number(chunk[idx - effective_offset]) |
|
1101 | 1104 | for idx, chunk in matches] |
|
1102 | 1105 | |
|
1103 | 1106 | return found_at_diff_lines |
|
1104 | 1107 | |
|
1105 | 1108 | def _get_file_diff(self, path): |
|
1106 | 1109 | for file_diff in self.parsed_diff: |
|
1107 | 1110 | if file_diff['filename'] == path: |
|
1108 | 1111 | break |
|
1109 | 1112 | else: |
|
1110 | 1113 | raise FileNotInDiffException("File {} not in diff".format(path)) |
|
1111 | 1114 | return file_diff |
|
1112 | 1115 | |
|
1113 | 1116 | def _find_chunk_line_index(self, file_diff, diff_line): |
|
1114 | 1117 | for chunk in file_diff['chunks']: |
|
1115 | 1118 | for idx, line in enumerate(chunk): |
|
1116 | 1119 | if line['old_lineno'] == diff_line.old: |
|
1117 | 1120 | return chunk, idx |
|
1118 | 1121 | if line['new_lineno'] == diff_line.new: |
|
1119 | 1122 | return chunk, idx |
|
1120 | 1123 | raise LineNotInDiffException( |
|
1121 | 1124 | "The line {} is not part of the diff.".format(diff_line)) |
|
1122 | 1125 | |
|
1123 | 1126 | |
|
1124 | 1127 | def _is_diff_content(line): |
|
1125 | 1128 | return line['action'] in ( |
|
1126 | 1129 | Action.UNMODIFIED, Action.ADD, Action.DELETE) |
|
1127 | 1130 | |
|
1128 | 1131 | |
|
1129 | 1132 | def _context_line(line): |
|
1130 | 1133 | return (line['action'], line['line']) |
|
1131 | 1134 | |
|
1132 | 1135 | |
|
1133 | 1136 | DiffLineNumber = collections.namedtuple('DiffLineNumber', ['old', 'new']) |
|
1134 | 1137 | |
|
1135 | 1138 | |
|
1136 | 1139 | def _line_to_diff_line_number(line): |
|
1137 | 1140 | new_line_no = line['new_lineno'] or None |
|
1138 | 1141 | old_line_no = line['old_lineno'] or None |
|
1139 | 1142 | return DiffLineNumber(old=old_line_no, new=new_line_no) |
|
1140 | 1143 | |
|
1141 | 1144 | |
|
1142 | 1145 | class FileNotInDiffException(Exception): |
|
1143 | 1146 | """ |
|
1144 | 1147 | Raised when the context for a missing file is requested. |
|
1145 | 1148 | |
|
1146 | 1149 | If you request the context for a line in a file which is not part of the |
|
1147 | 1150 | given diff, then this exception is raised. |
|
1148 | 1151 | """ |
|
1149 | 1152 | |
|
1150 | 1153 | |
|
1151 | 1154 | class LineNotInDiffException(Exception): |
|
1152 | 1155 | """ |
|
1153 | 1156 | Raised when the context for a missing line is requested. |
|
1154 | 1157 | |
|
1155 | 1158 | If you request the context for a line in a file and this line is not |
|
1156 | 1159 | part of the given diff, then this exception is raised. |
|
1157 | 1160 | """ |
|
1158 | 1161 | |
|
1159 | 1162 | |
|
1160 | 1163 | class DiffLimitExceeded(Exception): |
|
1161 | 1164 | pass |
@@ -1,525 +1,530 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | |
|
3 | 3 | # Copyright (C) 2011-2016 RhodeCode GmbH |
|
4 | 4 | # |
|
5 | 5 | # This program is free software: you can redistribute it and/or modify |
|
6 | 6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
7 | 7 | # (only), as published by the Free Software Foundation. |
|
8 | 8 | # |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 | 13 | # |
|
14 | 14 | # You should have received a copy of the GNU Affero General Public License |
|
15 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | 16 | # |
|
17 | 17 | # This program is dual-licensed. If you wish to learn more about the |
|
18 | 18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
19 | 19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
20 | 20 | |
|
21 | 21 | """ |
|
22 | 22 | comments model for RhodeCode |
|
23 | 23 | """ |
|
24 | 24 | |
|
25 | 25 | import logging |
|
26 | 26 | import traceback |
|
27 | 27 | import collections |
|
28 | 28 | |
|
29 | 29 | from datetime import datetime |
|
30 | 30 | |
|
31 | 31 | from pylons.i18n.translation import _ |
|
32 | 32 | from pyramid.threadlocal import get_current_registry |
|
33 | 33 | from sqlalchemy.sql.expression import null |
|
34 | 34 | from sqlalchemy.sql.functions import coalesce |
|
35 | 35 | |
|
36 | 36 | from rhodecode.lib import helpers as h, diffs |
|
37 | 37 | from rhodecode.lib.channelstream import channelstream_request |
|
38 | 38 | from rhodecode.lib.utils import action_logger |
|
39 | 39 | from rhodecode.lib.utils2 import extract_mentioned_users |
|
40 | 40 | from rhodecode.model import BaseModel |
|
41 | 41 | from rhodecode.model.db import ( |
|
42 | 42 | ChangesetComment, User, Notification, PullRequest) |
|
43 | 43 | from rhodecode.model.notification import NotificationModel |
|
44 | 44 | from rhodecode.model.meta import Session |
|
45 | 45 | from rhodecode.model.settings import VcsSettingsModel |
|
46 | 46 | from rhodecode.model.notification import EmailNotificationModel |
|
47 | 47 | |
|
48 | 48 | log = logging.getLogger(__name__) |
|
49 | 49 | |
|
50 | 50 | |
|
51 | 51 | class ChangesetCommentsModel(BaseModel): |
|
52 | 52 | |
|
53 | 53 | cls = ChangesetComment |
|
54 | 54 | |
|
55 | 55 | DIFF_CONTEXT_BEFORE = 3 |
|
56 | 56 | DIFF_CONTEXT_AFTER = 3 |
|
57 | 57 | |
|
58 | 58 | def __get_commit_comment(self, changeset_comment): |
|
59 | 59 | return self._get_instance(ChangesetComment, changeset_comment) |
|
60 | 60 | |
|
61 | 61 | def __get_pull_request(self, pull_request): |
|
62 | 62 | return self._get_instance(PullRequest, pull_request) |
|
63 | 63 | |
|
64 | 64 | def _extract_mentions(self, s): |
|
65 | 65 | user_objects = [] |
|
66 | 66 | for username in extract_mentioned_users(s): |
|
67 | 67 | user_obj = User.get_by_username(username, case_insensitive=True) |
|
68 | 68 | if user_obj: |
|
69 | 69 | user_objects.append(user_obj) |
|
70 | 70 | return user_objects |
|
71 | 71 | |
|
72 | 72 | def _get_renderer(self, global_renderer='rst'): |
|
73 | 73 | try: |
|
74 | 74 | # try reading from visual context |
|
75 | 75 | from pylons import tmpl_context |
|
76 | 76 | global_renderer = tmpl_context.visual.default_renderer |
|
77 | 77 | except AttributeError: |
|
78 | 78 | log.debug("Renderer not set, falling back " |
|
79 | 79 | "to default renderer '%s'", global_renderer) |
|
80 | 80 | except Exception: |
|
81 | 81 | log.error(traceback.format_exc()) |
|
82 | 82 | return global_renderer |
|
83 | 83 | |
|
84 | 84 | def create(self, text, repo, user, revision=None, pull_request=None, |
|
85 | 85 | f_path=None, line_no=None, status_change=None, |
|
86 | 86 | status_change_type=None, closing_pr=False, |
|
87 | 87 | send_email=True, renderer=None): |
|
88 | 88 | """ |
|
89 | 89 | Creates new comment for commit or pull request. |
|
90 | 90 | IF status_change is not none this comment is associated with a |
|
91 | 91 | status change of commit or commit associated with pull request |
|
92 | 92 | |
|
93 | 93 | :param text: |
|
94 | 94 | :param repo: |
|
95 | 95 | :param user: |
|
96 | 96 | :param revision: |
|
97 | 97 | :param pull_request: |
|
98 | 98 | :param f_path: |
|
99 | 99 | :param line_no: |
|
100 | 100 | :param status_change: Label for status change |
|
101 | 101 | :param status_change_type: type of status change |
|
102 | 102 | :param closing_pr: |
|
103 | 103 | :param send_email: |
|
104 | 104 | """ |
|
105 | 105 | if not text: |
|
106 | 106 | log.warning('Missing text for comment, skipping...') |
|
107 | 107 | return |
|
108 | 108 | |
|
109 | 109 | if not renderer: |
|
110 | 110 | renderer = self._get_renderer() |
|
111 | 111 | |
|
112 | 112 | repo = self._get_repo(repo) |
|
113 | 113 | user = self._get_user(user) |
|
114 | 114 | comment = ChangesetComment() |
|
115 | 115 | comment.renderer = renderer |
|
116 | 116 | comment.repo = repo |
|
117 | 117 | comment.author = user |
|
118 | 118 | comment.text = text |
|
119 | 119 | comment.f_path = f_path |
|
120 | 120 | comment.line_no = line_no |
|
121 | 121 | |
|
122 | 122 | #TODO (marcink): fix this and remove revision as param |
|
123 | 123 | commit_id = revision |
|
124 | 124 | pull_request_id = pull_request |
|
125 | 125 | |
|
126 | 126 | commit_obj = None |
|
127 | 127 | pull_request_obj = None |
|
128 | 128 | |
|
129 | 129 | if commit_id: |
|
130 | 130 | notification_type = EmailNotificationModel.TYPE_COMMIT_COMMENT |
|
131 | 131 | # do a lookup, so we don't pass something bad here |
|
132 | 132 | commit_obj = repo.scm_instance().get_commit(commit_id=commit_id) |
|
133 | 133 | comment.revision = commit_obj.raw_id |
|
134 | 134 | |
|
135 | 135 | elif pull_request_id: |
|
136 | 136 | notification_type = EmailNotificationModel.TYPE_PULL_REQUEST_COMMENT |
|
137 | 137 | pull_request_obj = self.__get_pull_request(pull_request_id) |
|
138 | 138 | comment.pull_request = pull_request_obj |
|
139 | 139 | else: |
|
140 | 140 | raise Exception('Please specify commit or pull_request_id') |
|
141 | 141 | |
|
142 | 142 | Session().add(comment) |
|
143 | 143 | Session().flush() |
|
144 | 144 | kwargs = { |
|
145 | 145 | 'user': user, |
|
146 | 146 | 'renderer_type': renderer, |
|
147 | 147 | 'repo_name': repo.repo_name, |
|
148 | 148 | 'status_change': status_change, |
|
149 | 149 | 'status_change_type': status_change_type, |
|
150 | 150 | 'comment_body': text, |
|
151 | 151 | 'comment_file': f_path, |
|
152 | 152 | 'comment_line': line_no, |
|
153 | 153 | } |
|
154 | 154 | |
|
155 | 155 | if commit_obj: |
|
156 | 156 | recipients = ChangesetComment.get_users( |
|
157 | 157 | revision=commit_obj.raw_id) |
|
158 | 158 | # add commit author if it's in RhodeCode system |
|
159 | 159 | cs_author = User.get_from_cs_author(commit_obj.author) |
|
160 | 160 | if not cs_author: |
|
161 | 161 | # use repo owner if we cannot extract the author correctly |
|
162 | 162 | cs_author = repo.user |
|
163 | 163 | recipients += [cs_author] |
|
164 | 164 | |
|
165 | 165 | commit_comment_url = self.get_url(comment) |
|
166 | 166 | |
|
167 | 167 | target_repo_url = h.link_to( |
|
168 | 168 | repo.repo_name, |
|
169 | 169 | h.url('summary_home', |
|
170 | 170 | repo_name=repo.repo_name, qualified=True)) |
|
171 | 171 | |
|
172 | 172 | # commit specifics |
|
173 | 173 | kwargs.update({ |
|
174 | 174 | 'commit': commit_obj, |
|
175 | 175 | 'commit_message': commit_obj.message, |
|
176 | 176 | 'commit_target_repo': target_repo_url, |
|
177 | 177 | 'commit_comment_url': commit_comment_url, |
|
178 | 178 | }) |
|
179 | 179 | |
|
180 | 180 | elif pull_request_obj: |
|
181 | 181 | # get the current participants of this pull request |
|
182 | 182 | recipients = ChangesetComment.get_users( |
|
183 | 183 | pull_request_id=pull_request_obj.pull_request_id) |
|
184 | 184 | # add pull request author |
|
185 | 185 | recipients += [pull_request_obj.author] |
|
186 | 186 | |
|
187 | 187 | # add the reviewers to notification |
|
188 | 188 | recipients += [x.user for x in pull_request_obj.reviewers] |
|
189 | 189 | |
|
190 | 190 | pr_target_repo = pull_request_obj.target_repo |
|
191 | 191 | pr_source_repo = pull_request_obj.source_repo |
|
192 | 192 | |
|
193 | 193 | pr_comment_url = h.url( |
|
194 | 194 | 'pullrequest_show', |
|
195 | 195 | repo_name=pr_target_repo.repo_name, |
|
196 | 196 | pull_request_id=pull_request_obj.pull_request_id, |
|
197 | 197 | anchor='comment-%s' % comment.comment_id, |
|
198 | 198 | qualified=True,) |
|
199 | 199 | |
|
200 | 200 | # set some variables for email notification |
|
201 | 201 | pr_target_repo_url = h.url( |
|
202 | 202 | 'summary_home', repo_name=pr_target_repo.repo_name, |
|
203 | 203 | qualified=True) |
|
204 | 204 | |
|
205 | 205 | pr_source_repo_url = h.url( |
|
206 | 206 | 'summary_home', repo_name=pr_source_repo.repo_name, |
|
207 | 207 | qualified=True) |
|
208 | 208 | |
|
209 | 209 | # pull request specifics |
|
210 | 210 | kwargs.update({ |
|
211 | 211 | 'pull_request': pull_request_obj, |
|
212 | 212 | 'pr_id': pull_request_obj.pull_request_id, |
|
213 | 213 | 'pr_target_repo': pr_target_repo, |
|
214 | 214 | 'pr_target_repo_url': pr_target_repo_url, |
|
215 | 215 | 'pr_source_repo': pr_source_repo, |
|
216 | 216 | 'pr_source_repo_url': pr_source_repo_url, |
|
217 | 217 | 'pr_comment_url': pr_comment_url, |
|
218 | 218 | 'pr_closing': closing_pr, |
|
219 | 219 | }) |
|
220 | 220 | if send_email: |
|
221 | 221 | # pre-generate the subject for notification itself |
|
222 | 222 | (subject, |
|
223 | 223 | _h, _e, # we don't care about those |
|
224 | 224 | body_plaintext) = EmailNotificationModel().render_email( |
|
225 | 225 | notification_type, **kwargs) |
|
226 | 226 | |
|
227 | 227 | mention_recipients = set( |
|
228 | 228 | self._extract_mentions(text)).difference(recipients) |
|
229 | 229 | |
|
230 | 230 | # create notification objects, and emails |
|
231 | 231 | NotificationModel().create( |
|
232 | 232 | created_by=user, |
|
233 | 233 | notification_subject=subject, |
|
234 | 234 | notification_body=body_plaintext, |
|
235 | 235 | notification_type=notification_type, |
|
236 | 236 | recipients=recipients, |
|
237 | 237 | mention_recipients=mention_recipients, |
|
238 | 238 | email_kwargs=kwargs, |
|
239 | 239 | ) |
|
240 | 240 | |
|
241 | 241 | action = ( |
|
242 | 242 | 'user_commented_pull_request:{}'.format( |
|
243 | 243 | comment.pull_request.pull_request_id) |
|
244 | 244 | if comment.pull_request |
|
245 | 245 | else 'user_commented_revision:{}'.format(comment.revision) |
|
246 | 246 | ) |
|
247 | 247 | action_logger(user, action, comment.repo) |
|
248 | 248 | |
|
249 | 249 | registry = get_current_registry() |
|
250 | 250 | rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {}) |
|
251 | 251 | channelstream_config = rhodecode_plugins.get('channelstream', {}) |
|
252 | 252 | msg_url = '' |
|
253 | 253 | if commit_obj: |
|
254 | 254 | msg_url = commit_comment_url |
|
255 | 255 | repo_name = repo.repo_name |
|
256 | 256 | elif pull_request_obj: |
|
257 | 257 | msg_url = pr_comment_url |
|
258 | 258 | repo_name = pr_target_repo.repo_name |
|
259 | 259 | |
|
260 | 260 | if channelstream_config.get('enabled'): |
|
261 | 261 | message = '<strong>{}</strong> {} - ' \ |
|
262 | 262 | '<a onclick="window.location=\'{}\';' \ |
|
263 | 263 | 'window.location.reload()">' \ |
|
264 | 264 | '<strong>{}</strong></a>' |
|
265 | 265 | message = message.format( |
|
266 | 266 | user.username, _('made a comment'), msg_url, |
|
267 | 267 | _('Show it now')) |
|
268 | 268 | channel = '/repo${}$/pr/{}'.format( |
|
269 | 269 | repo_name, |
|
270 | 270 | pull_request_id |
|
271 | 271 | ) |
|
272 | 272 | payload = { |
|
273 | 273 | 'type': 'message', |
|
274 | 274 | 'timestamp': datetime.utcnow(), |
|
275 | 275 | 'user': 'system', |
|
276 | 276 | 'exclude_users': [user.username], |
|
277 | 277 | 'channel': channel, |
|
278 | 278 | 'message': { |
|
279 | 279 | 'message': message, |
|
280 | 280 | 'level': 'info', |
|
281 | 281 | 'topic': '/notifications' |
|
282 | 282 | } |
|
283 | 283 | } |
|
284 | 284 | channelstream_request(channelstream_config, [payload], |
|
285 | 285 | '/message', raise_exc=False) |
|
286 | 286 | |
|
287 | 287 | return comment |
|
288 | 288 | |
|
289 | 289 | def delete(self, comment): |
|
290 | 290 | """ |
|
291 | 291 | Deletes given comment |
|
292 | 292 | |
|
293 | 293 | :param comment_id: |
|
294 | 294 | """ |
|
295 | 295 | comment = self.__get_commit_comment(comment) |
|
296 | 296 | Session().delete(comment) |
|
297 | 297 | |
|
298 | 298 | return comment |
|
299 | 299 | |
|
300 | 300 | def get_all_comments(self, repo_id, revision=None, pull_request=None): |
|
301 | 301 | q = ChangesetComment.query()\ |
|
302 | 302 | .filter(ChangesetComment.repo_id == repo_id) |
|
303 | 303 | if revision: |
|
304 | 304 | q = q.filter(ChangesetComment.revision == revision) |
|
305 | 305 | elif pull_request: |
|
306 | 306 | pull_request = self.__get_pull_request(pull_request) |
|
307 | 307 | q = q.filter(ChangesetComment.pull_request == pull_request) |
|
308 | 308 | else: |
|
309 | 309 | raise Exception('Please specify commit or pull_request') |
|
310 | 310 | q = q.order_by(ChangesetComment.created_on) |
|
311 | 311 | return q.all() |
|
312 | 312 | |
|
313 | 313 | def get_url(self, comment): |
|
314 | 314 | comment = self.__get_commit_comment(comment) |
|
315 | 315 | if comment.pull_request: |
|
316 | 316 | return h.url( |
|
317 | 317 | 'pullrequest_show', |
|
318 | 318 | repo_name=comment.pull_request.target_repo.repo_name, |
|
319 | 319 | pull_request_id=comment.pull_request.pull_request_id, |
|
320 | 320 | anchor='comment-%s' % comment.comment_id, |
|
321 | 321 | qualified=True,) |
|
322 | 322 | else: |
|
323 | 323 | return h.url( |
|
324 | 324 | 'changeset_home', |
|
325 | 325 | repo_name=comment.repo.repo_name, |
|
326 | 326 | revision=comment.revision, |
|
327 | 327 | anchor='comment-%s' % comment.comment_id, |
|
328 | 328 | qualified=True,) |
|
329 | 329 | |
|
330 | 330 | def get_comments(self, repo_id, revision=None, pull_request=None): |
|
331 | 331 | """ |
|
332 | 332 | Gets main comments based on revision or pull_request_id |
|
333 | 333 | |
|
334 | 334 | :param repo_id: |
|
335 | 335 | :param revision: |
|
336 | 336 | :param pull_request: |
|
337 | 337 | """ |
|
338 | 338 | |
|
339 | 339 | q = ChangesetComment.query()\ |
|
340 | 340 | .filter(ChangesetComment.repo_id == repo_id)\ |
|
341 | 341 | .filter(ChangesetComment.line_no == None)\ |
|
342 | 342 | .filter(ChangesetComment.f_path == None) |
|
343 | 343 | if revision: |
|
344 | 344 | q = q.filter(ChangesetComment.revision == revision) |
|
345 | 345 | elif pull_request: |
|
346 | 346 | pull_request = self.__get_pull_request(pull_request) |
|
347 | 347 | q = q.filter(ChangesetComment.pull_request == pull_request) |
|
348 | 348 | else: |
|
349 | 349 | raise Exception('Please specify commit or pull_request') |
|
350 | 350 | q = q.order_by(ChangesetComment.created_on) |
|
351 | 351 | return q.all() |
|
352 | 352 | |
|
353 | 353 | def get_inline_comments(self, repo_id, revision=None, pull_request=None): |
|
354 | 354 | q = self._get_inline_comments_query(repo_id, revision, pull_request) |
|
355 | 355 | return self._group_comments_by_path_and_line_number(q) |
|
356 | 356 | |
|
357 | 357 | def get_inline_comments_count(self, inline_comments, skip_outdated=True, |
|
358 | version=None): | |
|
358 | version=None, include_aggregates=False): | |
|
359 | version_aggregates = collections.defaultdict(list) | |
|
359 | 360 | inline_cnt = 0 |
|
360 | 361 | for fname, per_line_comments in inline_comments.iteritems(): |
|
361 | 362 | for lno, comments in per_line_comments.iteritems(): |
|
362 | inline_cnt += len( | |
|
363 | [comm for comm in comments | |
|
364 |
|
|
|
363 | for comm in comments: | |
|
364 | version_aggregates[comm.pull_request_version_id].append(comm) | |
|
365 | if not comm.outdated_at_version(version) and skip_outdated: | |
|
366 | inline_cnt += 1 | |
|
367 | ||
|
368 | if include_aggregates: | |
|
369 | return inline_cnt, version_aggregates | |
|
365 | 370 | return inline_cnt |
|
366 | 371 | |
|
367 | 372 | def get_outdated_comments(self, repo_id, pull_request): |
|
368 | 373 | # TODO: johbo: Remove `repo_id`, it is not needed to find the comments |
|
369 | 374 | # of a pull request. |
|
370 | 375 | q = self._all_inline_comments_of_pull_request(pull_request) |
|
371 | 376 | q = q.filter( |
|
372 | 377 | ChangesetComment.display_state == |
|
373 | 378 | ChangesetComment.COMMENT_OUTDATED |
|
374 | 379 | ).order_by(ChangesetComment.comment_id.asc()) |
|
375 | 380 | |
|
376 | 381 | return self._group_comments_by_path_and_line_number(q) |
|
377 | 382 | |
|
378 | 383 | def _get_inline_comments_query(self, repo_id, revision, pull_request): |
|
379 | 384 | # TODO: johbo: Split this into two methods: One for PR and one for |
|
380 | 385 | # commit. |
|
381 | 386 | if revision: |
|
382 | 387 | q = Session().query(ChangesetComment).filter( |
|
383 | 388 | ChangesetComment.repo_id == repo_id, |
|
384 | 389 | ChangesetComment.line_no != null(), |
|
385 | 390 | ChangesetComment.f_path != null(), |
|
386 | 391 | ChangesetComment.revision == revision) |
|
387 | 392 | |
|
388 | 393 | elif pull_request: |
|
389 | 394 | pull_request = self.__get_pull_request(pull_request) |
|
390 | 395 | if not ChangesetCommentsModel.use_outdated_comments(pull_request): |
|
391 | 396 | q = self._visible_inline_comments_of_pull_request(pull_request) |
|
392 | 397 | else: |
|
393 | 398 | q = self._all_inline_comments_of_pull_request(pull_request) |
|
394 | 399 | |
|
395 | 400 | else: |
|
396 | 401 | raise Exception('Please specify commit or pull_request_id') |
|
397 | 402 | q = q.order_by(ChangesetComment.comment_id.asc()) |
|
398 | 403 | return q |
|
399 | 404 | |
|
400 | 405 | def _group_comments_by_path_and_line_number(self, q): |
|
401 | 406 | comments = q.all() |
|
402 | 407 | paths = collections.defaultdict(lambda: collections.defaultdict(list)) |
|
403 | 408 | for co in comments: |
|
404 | 409 | paths[co.f_path][co.line_no].append(co) |
|
405 | 410 | return paths |
|
406 | 411 | |
|
407 | 412 | @classmethod |
|
408 | 413 | def needed_extra_diff_context(cls): |
|
409 | 414 | return max(cls.DIFF_CONTEXT_BEFORE, cls.DIFF_CONTEXT_AFTER) |
|
410 | 415 | |
|
411 | 416 | def outdate_comments(self, pull_request, old_diff_data, new_diff_data): |
|
412 | 417 | if not ChangesetCommentsModel.use_outdated_comments(pull_request): |
|
413 | 418 | return |
|
414 | 419 | |
|
415 | 420 | comments = self._visible_inline_comments_of_pull_request(pull_request) |
|
416 | 421 | comments_to_outdate = comments.all() |
|
417 | 422 | |
|
418 | 423 | for comment in comments_to_outdate: |
|
419 | 424 | self._outdate_one_comment(comment, old_diff_data, new_diff_data) |
|
420 | 425 | |
|
421 | 426 | def _outdate_one_comment(self, comment, old_diff_proc, new_diff_proc): |
|
422 | 427 | diff_line = _parse_comment_line_number(comment.line_no) |
|
423 | 428 | |
|
424 | 429 | try: |
|
425 | 430 | old_context = old_diff_proc.get_context_of_line( |
|
426 | 431 | path=comment.f_path, diff_line=diff_line) |
|
427 | 432 | new_context = new_diff_proc.get_context_of_line( |
|
428 | 433 | path=comment.f_path, diff_line=diff_line) |
|
429 | 434 | except (diffs.LineNotInDiffException, |
|
430 | 435 | diffs.FileNotInDiffException): |
|
431 | 436 | comment.display_state = ChangesetComment.COMMENT_OUTDATED |
|
432 | 437 | return |
|
433 | 438 | |
|
434 | 439 | if old_context == new_context: |
|
435 | 440 | return |
|
436 | 441 | |
|
437 | 442 | if self._should_relocate_diff_line(diff_line): |
|
438 | 443 | new_diff_lines = new_diff_proc.find_context( |
|
439 | 444 | path=comment.f_path, context=old_context, |
|
440 | 445 | offset=self.DIFF_CONTEXT_BEFORE) |
|
441 | 446 | if not new_diff_lines: |
|
442 | 447 | comment.display_state = ChangesetComment.COMMENT_OUTDATED |
|
443 | 448 | else: |
|
444 | 449 | new_diff_line = self._choose_closest_diff_line( |
|
445 | 450 | diff_line, new_diff_lines) |
|
446 | 451 | comment.line_no = _diff_to_comment_line_number(new_diff_line) |
|
447 | 452 | else: |
|
448 | 453 | comment.display_state = ChangesetComment.COMMENT_OUTDATED |
|
449 | 454 | |
|
450 | 455 | def _should_relocate_diff_line(self, diff_line): |
|
451 | 456 | """ |
|
452 | 457 | Checks if relocation shall be tried for the given `diff_line`. |
|
453 | 458 | |
|
454 | 459 | If a comment points into the first lines, then we can have a situation |
|
455 | 460 | that after an update another line has been added on top. In this case |
|
456 | 461 | we would find the context still and move the comment around. This |
|
457 | 462 | would be wrong. |
|
458 | 463 | """ |
|
459 | 464 | should_relocate = ( |
|
460 | 465 | (diff_line.new and diff_line.new > self.DIFF_CONTEXT_BEFORE) or |
|
461 | 466 | (diff_line.old and diff_line.old > self.DIFF_CONTEXT_BEFORE)) |
|
462 | 467 | return should_relocate |
|
463 | 468 | |
|
464 | 469 | def _choose_closest_diff_line(self, diff_line, new_diff_lines): |
|
465 | 470 | candidate = new_diff_lines[0] |
|
466 | 471 | best_delta = _diff_line_delta(diff_line, candidate) |
|
467 | 472 | for new_diff_line in new_diff_lines[1:]: |
|
468 | 473 | delta = _diff_line_delta(diff_line, new_diff_line) |
|
469 | 474 | if delta < best_delta: |
|
470 | 475 | candidate = new_diff_line |
|
471 | 476 | best_delta = delta |
|
472 | 477 | return candidate |
|
473 | 478 | |
|
474 | 479 | def _visible_inline_comments_of_pull_request(self, pull_request): |
|
475 | 480 | comments = self._all_inline_comments_of_pull_request(pull_request) |
|
476 | 481 | comments = comments.filter( |
|
477 | 482 | coalesce(ChangesetComment.display_state, '') != |
|
478 | 483 | ChangesetComment.COMMENT_OUTDATED) |
|
479 | 484 | return comments |
|
480 | 485 | |
|
481 | 486 | def _all_inline_comments_of_pull_request(self, pull_request): |
|
482 | 487 | comments = Session().query(ChangesetComment)\ |
|
483 | 488 | .filter(ChangesetComment.line_no != None)\ |
|
484 | 489 | .filter(ChangesetComment.f_path != None)\ |
|
485 | 490 | .filter(ChangesetComment.pull_request == pull_request) |
|
486 | 491 | return comments |
|
487 | 492 | |
|
488 | 493 | @staticmethod |
|
489 | 494 | def use_outdated_comments(pull_request): |
|
490 | 495 | settings_model = VcsSettingsModel(repo=pull_request.target_repo) |
|
491 | 496 | settings = settings_model.get_general_settings() |
|
492 | 497 | return settings.get('rhodecode_use_outdated_comments', False) |
|
493 | 498 | |
|
494 | 499 | |
|
495 | 500 | def _parse_comment_line_number(line_no): |
|
496 | 501 | """ |
|
497 | 502 | Parses line numbers of the form "(o|n)\d+" and returns them in a tuple. |
|
498 | 503 | """ |
|
499 | 504 | old_line = None |
|
500 | 505 | new_line = None |
|
501 | 506 | if line_no.startswith('o'): |
|
502 | 507 | old_line = int(line_no[1:]) |
|
503 | 508 | elif line_no.startswith('n'): |
|
504 | 509 | new_line = int(line_no[1:]) |
|
505 | 510 | else: |
|
506 | 511 | raise ValueError("Comment lines have to start with either 'o' or 'n'.") |
|
507 | 512 | return diffs.DiffLineNumber(old_line, new_line) |
|
508 | 513 | |
|
509 | 514 | |
|
510 | 515 | def _diff_to_comment_line_number(diff_line): |
|
511 | 516 | if diff_line.new is not None: |
|
512 | 517 | return u'n{}'.format(diff_line.new) |
|
513 | 518 | elif diff_line.old is not None: |
|
514 | 519 | return u'o{}'.format(diff_line.old) |
|
515 | 520 | return u'' |
|
516 | 521 | |
|
517 | 522 | |
|
518 | 523 | def _diff_line_delta(a, b): |
|
519 | 524 | if None not in (a.new, b.new): |
|
520 | 525 | return abs(a.new - b.new) |
|
521 | 526 | elif None not in (a.old, b.old): |
|
522 | 527 | return abs(a.old - b.old) |
|
523 | 528 | else: |
|
524 | 529 | raise ValueError( |
|
525 | 530 | "Cannot compute delta between {} and {}".format(a, b)) |
@@ -1,1187 +1,1199 b'' | |||
|
1 | 1 | // Default styles |
|
2 | 2 | |
|
3 | 3 | .diff-collapse { |
|
4 | 4 | margin: @padding 0; |
|
5 | 5 | text-align: right; |
|
6 | 6 | } |
|
7 | 7 | |
|
8 | 8 | .diff-container { |
|
9 | 9 | margin-bottom: @space; |
|
10 | 10 | |
|
11 | 11 | .diffblock { |
|
12 | 12 | margin-bottom: @space; |
|
13 | 13 | } |
|
14 | 14 | |
|
15 | 15 | &.hidden { |
|
16 | 16 | display: none; |
|
17 | 17 | overflow: hidden; |
|
18 | 18 | } |
|
19 | 19 | } |
|
20 | 20 | |
|
21 | 21 | .compare_view_files { |
|
22 | 22 | |
|
23 | 23 | .diff-container { |
|
24 | 24 | |
|
25 | 25 | .diffblock { |
|
26 | 26 | margin-bottom: 0; |
|
27 | 27 | } |
|
28 | 28 | } |
|
29 | 29 | } |
|
30 | 30 | |
|
31 | 31 | div.diffblock .sidebyside { |
|
32 | 32 | background: #ffffff; |
|
33 | 33 | } |
|
34 | 34 | |
|
35 | 35 | div.diffblock { |
|
36 | 36 | overflow-x: auto; |
|
37 | 37 | overflow-y: hidden; |
|
38 | 38 | clear: both; |
|
39 | 39 | padding: 0px; |
|
40 | 40 | background: @grey6; |
|
41 | 41 | border: @border-thickness solid @grey5; |
|
42 | 42 | -webkit-border-radius: @border-radius @border-radius 0px 0px; |
|
43 | 43 | border-radius: @border-radius @border-radius 0px 0px; |
|
44 | 44 | |
|
45 | 45 | |
|
46 | 46 | .comments-number { |
|
47 | 47 | float: right; |
|
48 | 48 | } |
|
49 | 49 | |
|
50 | 50 | // BEGIN CODE-HEADER STYLES |
|
51 | 51 | |
|
52 | 52 | .code-header { |
|
53 | 53 | background: @grey6; |
|
54 | 54 | padding: 10px 0 10px 0; |
|
55 | 55 | height: auto; |
|
56 | 56 | width: 100%; |
|
57 | 57 | |
|
58 | 58 | .hash { |
|
59 | 59 | float: left; |
|
60 | 60 | padding: 2px 0 0 2px; |
|
61 | 61 | } |
|
62 | 62 | |
|
63 | 63 | .date { |
|
64 | 64 | float: left; |
|
65 | 65 | text-transform: uppercase; |
|
66 | 66 | padding: 4px 0px 0px 2px; |
|
67 | 67 | } |
|
68 | 68 | |
|
69 | 69 | div { |
|
70 | 70 | margin-left: 4px; |
|
71 | 71 | } |
|
72 | 72 | |
|
73 | 73 | div.compare_header { |
|
74 | 74 | min-height: 40px; |
|
75 | 75 | margin: 0; |
|
76 | 76 | padding: 0 @padding; |
|
77 | 77 | |
|
78 | 78 | .drop-menu { |
|
79 | 79 | float:left; |
|
80 | 80 | display: block; |
|
81 | 81 | margin:0 0 @padding 0; |
|
82 | 82 | } |
|
83 | 83 | |
|
84 | 84 | .compare-label { |
|
85 | 85 | float: left; |
|
86 | 86 | clear: both; |
|
87 | 87 | display: inline-block; |
|
88 | 88 | min-width: 5em; |
|
89 | 89 | margin: 0; |
|
90 | 90 | padding: @button-padding @button-padding @button-padding 0; |
|
91 | 91 | font-family: @text-semibold; |
|
92 | 92 | } |
|
93 | 93 | |
|
94 | 94 | .compare-buttons { |
|
95 | 95 | float: left; |
|
96 | 96 | margin: 0; |
|
97 | 97 | padding: 0 0 @padding; |
|
98 | 98 | |
|
99 | 99 | .btn { |
|
100 | 100 | margin: 0 @padding 0 0; |
|
101 | 101 | } |
|
102 | 102 | } |
|
103 | 103 | } |
|
104 | 104 | |
|
105 | 105 | } |
|
106 | 106 | |
|
107 | 107 | .parents { |
|
108 | 108 | float: left; |
|
109 | 109 | width: 100px; |
|
110 | 110 | font-weight: 400; |
|
111 | 111 | vertical-align: middle; |
|
112 | 112 | padding: 0px 2px 0px 2px; |
|
113 | 113 | background-color: @grey6; |
|
114 | 114 | |
|
115 | 115 | #parent_link { |
|
116 | 116 | margin: 00px 2px; |
|
117 | 117 | |
|
118 | 118 | &.double { |
|
119 | 119 | margin: 0px 2px; |
|
120 | 120 | } |
|
121 | 121 | |
|
122 | 122 | &.disabled{ |
|
123 | 123 | margin-right: @padding; |
|
124 | 124 | } |
|
125 | 125 | } |
|
126 | 126 | } |
|
127 | 127 | |
|
128 | 128 | .children { |
|
129 | 129 | float: right; |
|
130 | 130 | width: 100px; |
|
131 | 131 | font-weight: 400; |
|
132 | 132 | vertical-align: middle; |
|
133 | 133 | text-align: right; |
|
134 | 134 | padding: 0px 2px 0px 2px; |
|
135 | 135 | background-color: @grey6; |
|
136 | 136 | |
|
137 | 137 | #child_link { |
|
138 | 138 | margin: 0px 2px; |
|
139 | 139 | |
|
140 | 140 | &.double { |
|
141 | 141 | margin: 0px 2px; |
|
142 | 142 | } |
|
143 | 143 | |
|
144 | 144 | &.disabled{ |
|
145 | 145 | margin-right: @padding; |
|
146 | 146 | } |
|
147 | 147 | } |
|
148 | 148 | } |
|
149 | 149 | |
|
150 | 150 | .changeset_header { |
|
151 | 151 | height: 16px; |
|
152 | 152 | |
|
153 | 153 | & > div{ |
|
154 | 154 | margin-right: @padding; |
|
155 | 155 | } |
|
156 | 156 | } |
|
157 | 157 | |
|
158 | 158 | .changeset_file { |
|
159 | 159 | text-align: left; |
|
160 | 160 | float: left; |
|
161 | 161 | padding: 0; |
|
162 | 162 | |
|
163 | 163 | a{ |
|
164 | 164 | display: inline-block; |
|
165 | 165 | margin-right: 0.5em; |
|
166 | 166 | } |
|
167 | 167 | |
|
168 | 168 | #selected_mode{ |
|
169 | 169 | margin-left: 0; |
|
170 | 170 | } |
|
171 | 171 | } |
|
172 | 172 | |
|
173 | 173 | .diff-menu-wrapper { |
|
174 | 174 | float: left; |
|
175 | 175 | } |
|
176 | 176 | |
|
177 | 177 | .diff-menu { |
|
178 | 178 | position: absolute; |
|
179 | 179 | background: none repeat scroll 0 0 #FFFFFF; |
|
180 | 180 | border-color: #003367 @grey3 @grey3; |
|
181 | 181 | border-right: 1px solid @grey3; |
|
182 | 182 | border-style: solid solid solid; |
|
183 | 183 | border-width: @border-thickness; |
|
184 | 184 | box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2); |
|
185 | 185 | margin-top: 5px; |
|
186 | 186 | margin-left: 1px; |
|
187 | 187 | } |
|
188 | 188 | |
|
189 | 189 | .diff-actions, .editor-actions { |
|
190 | 190 | float: left; |
|
191 | 191 | |
|
192 | 192 | input{ |
|
193 | 193 | margin: 0 0.5em 0 0; |
|
194 | 194 | } |
|
195 | 195 | } |
|
196 | 196 | |
|
197 | 197 | // END CODE-HEADER STYLES |
|
198 | 198 | |
|
199 | 199 | // BEGIN CODE-BODY STYLES |
|
200 | 200 | |
|
201 | 201 | .code-body { |
|
202 | 202 | background: white; |
|
203 | 203 | padding: 0; |
|
204 | 204 | background-color: #ffffff; |
|
205 | 205 | position: relative; |
|
206 | 206 | max-width: none; |
|
207 | 207 | box-sizing: border-box; |
|
208 | 208 | // TODO: johbo: Parent has overflow: auto, this forces the child here |
|
209 | 209 | // to have the intended size and to scroll. Should be simplified. |
|
210 | 210 | width: 100%; |
|
211 | 211 | overflow-x: auto; |
|
212 | 212 | } |
|
213 | 213 | |
|
214 | 214 | pre.raw { |
|
215 | 215 | background: white; |
|
216 | 216 | color: @grey1; |
|
217 | 217 | } |
|
218 | 218 | // END CODE-BODY STYLES |
|
219 | 219 | |
|
220 | 220 | } |
|
221 | 221 | |
|
222 | 222 | |
|
223 | 223 | table.code-difftable { |
|
224 | 224 | border-collapse: collapse; |
|
225 | 225 | width: 99%; |
|
226 | 226 | border-radius: 0px !important; |
|
227 | 227 | |
|
228 | 228 | td { |
|
229 | 229 | padding: 0 !important; |
|
230 | 230 | background: none !important; |
|
231 | 231 | border: 0 !important; |
|
232 | 232 | } |
|
233 | 233 | |
|
234 | 234 | .context { |
|
235 | 235 | background: none repeat scroll 0 0 #DDE7EF; |
|
236 | 236 | } |
|
237 | 237 | |
|
238 | 238 | .add { |
|
239 | 239 | background: none repeat scroll 0 0 #DDFFDD; |
|
240 | 240 | |
|
241 | 241 | ins { |
|
242 | 242 | background: none repeat scroll 0 0 #AAFFAA; |
|
243 | 243 | text-decoration: none; |
|
244 | 244 | } |
|
245 | 245 | } |
|
246 | 246 | |
|
247 | 247 | .del { |
|
248 | 248 | background: none repeat scroll 0 0 #FFDDDD; |
|
249 | 249 | |
|
250 | 250 | del { |
|
251 | 251 | background: none repeat scroll 0 0 #FFAAAA; |
|
252 | 252 | text-decoration: none; |
|
253 | 253 | } |
|
254 | 254 | } |
|
255 | 255 | |
|
256 | 256 | /** LINE NUMBERS **/ |
|
257 | 257 | .lineno { |
|
258 | 258 | padding-left: 2px !important; |
|
259 | 259 | padding-right: 2px; |
|
260 | 260 | text-align: right; |
|
261 | 261 | width: 32px; |
|
262 | 262 | -moz-user-select: none; |
|
263 | 263 | -webkit-user-select: none; |
|
264 | 264 | border-right: @border-thickness solid @grey5 !important; |
|
265 | 265 | border-left: 0px solid #CCC !important; |
|
266 | 266 | border-top: 0px solid #CCC !important; |
|
267 | 267 | border-bottom: none !important; |
|
268 | 268 | |
|
269 | 269 | a { |
|
270 | 270 | &:extend(pre); |
|
271 | 271 | text-align: right; |
|
272 | 272 | padding-right: 2px; |
|
273 | 273 | cursor: pointer; |
|
274 | 274 | display: block; |
|
275 | 275 | width: 32px; |
|
276 | 276 | } |
|
277 | 277 | } |
|
278 | 278 | |
|
279 | 279 | .context { |
|
280 | 280 | cursor: auto; |
|
281 | 281 | &:extend(pre); |
|
282 | 282 | } |
|
283 | 283 | |
|
284 | 284 | .lineno-inline { |
|
285 | 285 | background: none repeat scroll 0 0 #FFF !important; |
|
286 | 286 | padding-left: 2px; |
|
287 | 287 | padding-right: 2px; |
|
288 | 288 | text-align: right; |
|
289 | 289 | width: 30px; |
|
290 | 290 | -moz-user-select: none; |
|
291 | 291 | -webkit-user-select: none; |
|
292 | 292 | } |
|
293 | 293 | |
|
294 | 294 | /** CODE **/ |
|
295 | 295 | .code { |
|
296 | 296 | display: block; |
|
297 | 297 | width: 100%; |
|
298 | 298 | |
|
299 | 299 | td { |
|
300 | 300 | margin: 0; |
|
301 | 301 | padding: 0; |
|
302 | 302 | } |
|
303 | 303 | |
|
304 | 304 | pre { |
|
305 | 305 | margin: 0; |
|
306 | 306 | padding: 0; |
|
307 | 307 | margin-left: .5em; |
|
308 | 308 | } |
|
309 | 309 | } |
|
310 | 310 | } |
|
311 | 311 | |
|
312 | 312 | |
|
313 | 313 | // Comments |
|
314 | 314 | |
|
315 | 315 | div.comment:target { |
|
316 | 316 | border-left: 6px solid @comment-highlight-color !important; |
|
317 | 317 | padding-left: 3px; |
|
318 | 318 | margin-left: -9px; |
|
319 | 319 | } |
|
320 | 320 | |
|
321 | 321 | //TODO: anderson: can't get an absolute number out of anything, so had to put the |
|
322 | 322 | //current values that might change. But to make it clear I put as a calculation |
|
323 | 323 | @comment-max-width: 1065px; |
|
324 | 324 | @pr-extra-margin: 34px; |
|
325 | 325 | @pr-border-spacing: 4px; |
|
326 | 326 | @pr-comment-width: @comment-max-width - @pr-extra-margin - @pr-border-spacing; |
|
327 | 327 | |
|
328 | 328 | // Pull Request |
|
329 | 329 | .cs_files .code-difftable { |
|
330 | 330 | border: @border-thickness solid @grey5; //borders only on PRs |
|
331 | 331 | |
|
332 | 332 | .comment-inline-form, |
|
333 | 333 | div.comment { |
|
334 | 334 | width: @pr-comment-width; |
|
335 | 335 | } |
|
336 | 336 | } |
|
337 | 337 | |
|
338 | 338 | // Changeset |
|
339 | 339 | .code-difftable { |
|
340 | 340 | .comment-inline-form, |
|
341 | 341 | div.comment { |
|
342 | 342 | width: @comment-max-width; |
|
343 | 343 | } |
|
344 | 344 | } |
|
345 | 345 | |
|
346 | 346 | //Style page |
|
347 | 347 | @style-extra-margin: @sidebar-width + (@sidebarpadding * 3) + @padding; |
|
348 | 348 | #style-page .code-difftable{ |
|
349 | 349 | .comment-inline-form, |
|
350 | 350 | div.comment { |
|
351 | 351 | width: @comment-max-width - @style-extra-margin; |
|
352 | 352 | } |
|
353 | 353 | } |
|
354 | 354 | |
|
355 | 355 | #context-bar > h2 { |
|
356 | 356 | font-size: 20px; |
|
357 | 357 | } |
|
358 | 358 | |
|
359 | 359 | #context-bar > h2> a { |
|
360 | 360 | font-size: 20px; |
|
361 | 361 | } |
|
362 | 362 | // end of defaults |
|
363 | 363 | |
|
364 | 364 | .file_diff_buttons { |
|
365 | 365 | padding: 0 0 @padding; |
|
366 | 366 | |
|
367 | 367 | .drop-menu { |
|
368 | 368 | float: left; |
|
369 | 369 | margin: 0 @padding 0 0; |
|
370 | 370 | } |
|
371 | 371 | .btn { |
|
372 | 372 | margin: 0 @padding 0 0; |
|
373 | 373 | } |
|
374 | 374 | } |
|
375 | 375 | |
|
376 | 376 | .code-body.textarea.editor { |
|
377 | 377 | max-width: none; |
|
378 | 378 | padding: 15px; |
|
379 | 379 | } |
|
380 | 380 | |
|
381 | 381 | td.injected_diff{ |
|
382 | 382 | max-width: 1178px; |
|
383 | 383 | overflow-x: auto; |
|
384 | 384 | overflow-y: hidden; |
|
385 | 385 | |
|
386 | 386 | div.diff-container, |
|
387 | 387 | div.diffblock{ |
|
388 | 388 | max-width: 100%; |
|
389 | 389 | } |
|
390 | 390 | |
|
391 | 391 | div.code-body { |
|
392 | 392 | max-width: 1124px; |
|
393 | 393 | overflow-x: auto; |
|
394 | 394 | overflow-y: hidden; |
|
395 | 395 | padding: 0; |
|
396 | 396 | } |
|
397 | 397 | div.diffblock { |
|
398 | 398 | border: none; |
|
399 | 399 | } |
|
400 | 400 | |
|
401 | 401 | &.inline-form { |
|
402 | 402 | width: 99% |
|
403 | 403 | } |
|
404 | 404 | } |
|
405 | 405 | |
|
406 | 406 | |
|
407 | 407 | table.code-difftable { |
|
408 | 408 | width: 100%; |
|
409 | 409 | } |
|
410 | 410 | |
|
411 | 411 | /** PYGMENTS COLORING **/ |
|
412 | 412 | div.codeblock { |
|
413 | 413 | |
|
414 | 414 | // TODO: johbo: Added interim to get rid of the margin around |
|
415 | 415 | // Select2 widgets. This needs further cleanup. |
|
416 | 416 | margin-top: @padding; |
|
417 | 417 | |
|
418 | 418 | overflow: auto; |
|
419 | 419 | padding: 0px; |
|
420 | 420 | border: @border-thickness solid @grey5; |
|
421 | 421 | background: @grey6; |
|
422 | 422 | .border-radius(@border-radius); |
|
423 | 423 | |
|
424 | 424 | #remove_gist { |
|
425 | 425 | float: right; |
|
426 | 426 | } |
|
427 | 427 | |
|
428 | 428 | .author { |
|
429 | 429 | clear: both; |
|
430 | 430 | vertical-align: middle; |
|
431 | 431 | font-family: @text-bold; |
|
432 | 432 | } |
|
433 | 433 | |
|
434 | 434 | .btn-mini { |
|
435 | 435 | float: left; |
|
436 | 436 | margin: 0 5px 0 0; |
|
437 | 437 | } |
|
438 | 438 | |
|
439 | 439 | .code-header { |
|
440 | 440 | padding: @padding; |
|
441 | 441 | border-bottom: @border-thickness solid @grey5; |
|
442 | 442 | |
|
443 | 443 | .rc-user { |
|
444 | 444 | min-width: 0; |
|
445 | 445 | margin-right: .5em; |
|
446 | 446 | } |
|
447 | 447 | |
|
448 | 448 | .stats { |
|
449 | 449 | clear: both; |
|
450 | 450 | margin: 0 0 @padding 0; |
|
451 | 451 | padding: 0; |
|
452 | 452 | .left { |
|
453 | 453 | float: left; |
|
454 | 454 | clear: left; |
|
455 | 455 | max-width: 75%; |
|
456 | 456 | margin: 0 0 @padding 0; |
|
457 | 457 | |
|
458 | 458 | &.item { |
|
459 | 459 | margin-right: @padding; |
|
460 | 460 | &.last { border-right: none; } |
|
461 | 461 | } |
|
462 | 462 | } |
|
463 | 463 | .buttons { float: right; } |
|
464 | 464 | .author { |
|
465 | 465 | height: 25px; margin-left: 15px; font-weight: bold; |
|
466 | 466 | } |
|
467 | 467 | } |
|
468 | 468 | |
|
469 | 469 | .commit { |
|
470 | 470 | margin: 5px 0 0 26px; |
|
471 | 471 | font-weight: normal; |
|
472 | 472 | white-space: pre-wrap; |
|
473 | 473 | } |
|
474 | 474 | } |
|
475 | 475 | |
|
476 | 476 | .message { |
|
477 | 477 | position: relative; |
|
478 | 478 | margin: @padding; |
|
479 | 479 | |
|
480 | 480 | .codeblock-label { |
|
481 | 481 | margin: 0 0 1em 0; |
|
482 | 482 | } |
|
483 | 483 | } |
|
484 | 484 | |
|
485 | 485 | .code-body { |
|
486 | 486 | padding: @padding; |
|
487 | 487 | background-color: #ffffff; |
|
488 | 488 | min-width: 100%; |
|
489 | 489 | box-sizing: border-box; |
|
490 | 490 | // TODO: johbo: Parent has overflow: auto, this forces the child here |
|
491 | 491 | // to have the intended size and to scroll. Should be simplified. |
|
492 | 492 | width: 100%; |
|
493 | 493 | overflow-x: auto; |
|
494 | 494 | } |
|
495 | 495 | } |
|
496 | 496 | |
|
497 | 497 | .code-highlighttable, |
|
498 | 498 | div.codeblock { |
|
499 | 499 | |
|
500 | 500 | &.readme { |
|
501 | 501 | background-color: white; |
|
502 | 502 | } |
|
503 | 503 | |
|
504 | 504 | .markdown-block table { |
|
505 | 505 | border-collapse: collapse; |
|
506 | 506 | |
|
507 | 507 | th, |
|
508 | 508 | td { |
|
509 | 509 | padding: .5em; |
|
510 | 510 | border: @border-thickness solid @border-default-color; |
|
511 | 511 | } |
|
512 | 512 | } |
|
513 | 513 | |
|
514 | 514 | table { |
|
515 | 515 | border: 0px; |
|
516 | 516 | margin: 0; |
|
517 | 517 | letter-spacing: normal; |
|
518 | 518 | |
|
519 | 519 | |
|
520 | 520 | td { |
|
521 | 521 | border: 0px; |
|
522 | 522 | vertical-align: top; |
|
523 | 523 | } |
|
524 | 524 | } |
|
525 | 525 | } |
|
526 | 526 | |
|
527 | 527 | div.codeblock .code-header .search-path { padding: 0 0 0 10px; } |
|
528 | 528 | div.search-code-body { |
|
529 | 529 | background-color: #ffffff; padding: 5px 0 5px 10px; |
|
530 | 530 | pre { |
|
531 | 531 | .match { background-color: #faffa6;} |
|
532 | 532 | .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; } |
|
533 | 533 | } |
|
534 | 534 | .code-highlighttable { |
|
535 | 535 | border-collapse: collapse; |
|
536 | 536 | |
|
537 | 537 | tr:hover { |
|
538 | 538 | background: #fafafa; |
|
539 | 539 | } |
|
540 | 540 | td.code { |
|
541 | 541 | padding-left: 10px; |
|
542 | 542 | } |
|
543 | 543 | td.line { |
|
544 | 544 | border-right: 1px solid #ccc !important; |
|
545 | 545 | padding-right: 10px; |
|
546 | 546 | text-align: right; |
|
547 | 547 | font-family: "Lucida Console",Monaco,monospace; |
|
548 | 548 | span { |
|
549 | 549 | white-space: pre-wrap; |
|
550 | 550 | color: #666666; |
|
551 | 551 | } |
|
552 | 552 | } |
|
553 | 553 | } |
|
554 | 554 | } |
|
555 | 555 | |
|
556 | 556 | div.annotatediv { margin-left: 2px; margin-right: 4px; } |
|
557 | 557 | .code-highlight { |
|
558 | 558 | margin: 0; padding: 0; border-left: @border-thickness solid @grey5; |
|
559 | 559 | pre, .linenodiv pre { padding: 0 5px; margin: 0; } |
|
560 | 560 | pre div:target {background-color: @comment-highlight-color !important;} |
|
561 | 561 | } |
|
562 | 562 | |
|
563 | 563 | .linenos a { text-decoration: none; } |
|
564 | 564 | |
|
565 | 565 | .CodeMirror-selected { background: @rchighlightblue; } |
|
566 | 566 | .CodeMirror-focused .CodeMirror-selected { background: @rchighlightblue; } |
|
567 | 567 | .CodeMirror ::selection { background: @rchighlightblue; } |
|
568 | 568 | .CodeMirror ::-moz-selection { background: @rchighlightblue; } |
|
569 | 569 | |
|
570 | 570 | .code { display: block; border:0px !important; } |
|
571 | 571 | .code-highlight, /* TODO: dan: merge codehilite into code-highlight */ |
|
572 | 572 | .codehilite { |
|
573 | 573 | .hll { background-color: #ffffcc } |
|
574 | 574 | .c { color: #408080; font-style: italic } /* Comment */ |
|
575 | 575 | .err, .codehilite .err { border: @border-thickness solid #FF0000 } /* Error */ |
|
576 | 576 | .k { color: #008000; font-weight: bold } /* Keyword */ |
|
577 | 577 | .o { color: #666666 } /* Operator */ |
|
578 | 578 | .cm { color: #408080; font-style: italic } /* Comment.Multiline */ |
|
579 | 579 | .cp { color: #BC7A00 } /* Comment.Preproc */ |
|
580 | 580 | .c1 { color: #408080; font-style: italic } /* Comment.Single */ |
|
581 | 581 | .cs { color: #408080; font-style: italic } /* Comment.Special */ |
|
582 | 582 | .gd { color: #A00000 } /* Generic.Deleted */ |
|
583 | 583 | .ge { font-style: italic } /* Generic.Emph */ |
|
584 | 584 | .gr { color: #FF0000 } /* Generic.Error */ |
|
585 | 585 | .gh { color: #000080; font-weight: bold } /* Generic.Heading */ |
|
586 | 586 | .gi { color: #00A000 } /* Generic.Inserted */ |
|
587 | 587 | .go { color: #808080 } /* Generic.Output */ |
|
588 | 588 | .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ |
|
589 | 589 | .gs { font-weight: bold } /* Generic.Strong */ |
|
590 | 590 | .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ |
|
591 | 591 | .gt { color: #0040D0 } /* Generic.Traceback */ |
|
592 | 592 | .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ |
|
593 | 593 | .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ |
|
594 | 594 | .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ |
|
595 | 595 | .kp { color: #008000 } /* Keyword.Pseudo */ |
|
596 | 596 | .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ |
|
597 | 597 | .kt { color: #B00040 } /* Keyword.Type */ |
|
598 | 598 | .m { color: #666666 } /* Literal.Number */ |
|
599 | 599 | .s { color: #BA2121 } /* Literal.String */ |
|
600 | 600 | .na { color: #7D9029 } /* Name.Attribute */ |
|
601 | 601 | .nb { color: #008000 } /* Name.Builtin */ |
|
602 | 602 | .nc { color: #0000FF; font-weight: bold } /* Name.Class */ |
|
603 | 603 | .no { color: #880000 } /* Name.Constant */ |
|
604 | 604 | .nd { color: #AA22FF } /* Name.Decorator */ |
|
605 | 605 | .ni { color: #999999; font-weight: bold } /* Name.Entity */ |
|
606 | 606 | .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ |
|
607 | 607 | .nf { color: #0000FF } /* Name.Function */ |
|
608 | 608 | .nl { color: #A0A000 } /* Name.Label */ |
|
609 | 609 | .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ |
|
610 | 610 | .nt { color: #008000; font-weight: bold } /* Name.Tag */ |
|
611 | 611 | .nv { color: #19177C } /* Name.Variable */ |
|
612 | 612 | .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ |
|
613 | 613 | .w { color: #bbbbbb } /* Text.Whitespace */ |
|
614 | 614 | .mf { color: #666666 } /* Literal.Number.Float */ |
|
615 | 615 | .mh { color: #666666 } /* Literal.Number.Hex */ |
|
616 | 616 | .mi { color: #666666 } /* Literal.Number.Integer */ |
|
617 | 617 | .mo { color: #666666 } /* Literal.Number.Oct */ |
|
618 | 618 | .sb { color: #BA2121 } /* Literal.String.Backtick */ |
|
619 | 619 | .sc { color: #BA2121 } /* Literal.String.Char */ |
|
620 | 620 | .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ |
|
621 | 621 | .s2 { color: #BA2121 } /* Literal.String.Double */ |
|
622 | 622 | .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ |
|
623 | 623 | .sh { color: #BA2121 } /* Literal.String.Heredoc */ |
|
624 | 624 | .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ |
|
625 | 625 | .sx { color: #008000 } /* Literal.String.Other */ |
|
626 | 626 | .sr { color: #BB6688 } /* Literal.String.Regex */ |
|
627 | 627 | .s1 { color: #BA2121 } /* Literal.String.Single */ |
|
628 | 628 | .ss { color: #19177C } /* Literal.String.Symbol */ |
|
629 | 629 | .bp { color: #008000 } /* Name.Builtin.Pseudo */ |
|
630 | 630 | .vc { color: #19177C } /* Name.Variable.Class */ |
|
631 | 631 | .vg { color: #19177C } /* Name.Variable.Global */ |
|
632 | 632 | .vi { color: #19177C } /* Name.Variable.Instance */ |
|
633 | 633 | .il { color: #666666 } /* Literal.Number.Integer.Long */ |
|
634 | 634 | } |
|
635 | 635 | |
|
636 | 636 | /* customized pre blocks for markdown/rst */ |
|
637 | 637 | pre.literal-block, .codehilite pre{ |
|
638 | 638 | padding: @padding; |
|
639 | 639 | border: 1px solid @grey6; |
|
640 | 640 | .border-radius(@border-radius); |
|
641 | 641 | background-color: @grey7; |
|
642 | 642 | } |
|
643 | 643 | |
|
644 | 644 | |
|
645 | 645 | /* START NEW CODE BLOCK CSS */ |
|
646 | 646 | |
|
647 | 647 | @cb-line-height: 18px; |
|
648 | 648 | @cb-line-code-padding: 10px; |
|
649 | 649 | @cb-text-padding: 5px; |
|
650 | 650 | |
|
651 | 651 | @pill-padding: 2px 7px; |
|
652 | 652 | |
|
653 | 653 | input.filediff-collapse-state { |
|
654 | 654 | display: none; |
|
655 | 655 | |
|
656 | 656 | &:checked + .filediff { /* file diff is collapsed */ |
|
657 | 657 | .cb { |
|
658 | 658 | display: none |
|
659 | 659 | } |
|
660 | 660 | .filediff-collapse-indicator { |
|
661 |
|
|
|
662 | border-color: transparent transparent transparent #ccc; | |
|
661 | width: 0; | |
|
662 | height: 0; | |
|
663 | border-style: solid; | |
|
664 | border-width: 6.5px 0 6.5px 11.3px; | |
|
665 | border-color: transparent transparent transparent #ccc; | |
|
663 | 666 | } |
|
664 | 667 | .filediff-menu { |
|
665 | 668 | display: none; |
|
666 | 669 | } |
|
667 |
margin: |
|
|
670 | margin: 10px 0 0 0; | |
|
668 | 671 | } |
|
669 | 672 | |
|
670 | 673 | &+ .filediff { /* file diff is expanded */ |
|
671 | 674 | .filediff-collapse-indicator { |
|
672 |
|
|
|
675 | width: 0; | |
|
676 | height: 0; | |
|
677 | border-style: solid; | |
|
678 | border-width: 11.3px 6.5px 0 6.5px; | |
|
673 | 679 | border-color: #ccc transparent transparent transparent; |
|
674 | 680 | } |
|
675 | 681 | .filediff-menu { |
|
676 | 682 | display: block; |
|
677 | 683 | } |
|
678 |
margin: |
|
|
684 | margin: 10px 0; | |
|
679 | 685 | &:nth-child(2) { |
|
680 | 686 | margin: 0; |
|
681 | 687 | } |
|
682 | 688 | } |
|
683 | 689 | } |
|
684 | 690 | .cs_files { |
|
685 | 691 | clear: both; |
|
686 | 692 | } |
|
687 | 693 | |
|
688 | 694 | .diffset-menu { |
|
689 | 695 | margin-bottom: 20px; |
|
690 | 696 | } |
|
691 | 697 | .diffset { |
|
692 | 698 | margin: 20px auto; |
|
693 | 699 | .diffset-heading { |
|
694 | 700 | border: 1px solid @grey5; |
|
695 | 701 | margin-bottom: -1px; |
|
696 | 702 | // margin-top: 20px; |
|
697 | 703 | h2 { |
|
698 | 704 | margin: 0; |
|
699 | 705 | line-height: 38px; |
|
700 | 706 | padding-left: 10px; |
|
701 | 707 | } |
|
702 | 708 | .btn { |
|
703 | 709 | margin: 0; |
|
704 | 710 | } |
|
705 | 711 | background: @grey6; |
|
706 | 712 | display: block; |
|
707 | 713 | padding: 5px; |
|
708 | 714 | } |
|
709 | 715 | .diffset-heading-warning { |
|
710 | 716 | background: @alert3-inner; |
|
711 | 717 | border: 1px solid @alert3; |
|
712 | 718 | } |
|
713 | 719 | &.diffset-comments-disabled { |
|
714 | 720 | .cb-comment-box-opener, .comment-inline-form, .cb-comment-add-button { |
|
715 | 721 | display: none !important; |
|
716 | 722 | } |
|
717 | 723 | } |
|
718 | 724 | } |
|
719 | 725 | |
|
720 | 726 | .pill { |
|
721 | 727 | display: block; |
|
722 | 728 | float: left; |
|
723 | 729 | padding: @pill-padding; |
|
724 | 730 | } |
|
725 | 731 | .pill-group { |
|
726 | 732 | .pill { |
|
727 | 733 | opacity: .8; |
|
728 | 734 | &:first-child { |
|
729 | 735 | border-radius: @border-radius 0 0 @border-radius; |
|
730 | 736 | } |
|
731 | 737 | &:last-child { |
|
732 | 738 | border-radius: 0 @border-radius @border-radius 0; |
|
733 | 739 | } |
|
734 | 740 | &:only-child { |
|
735 | 741 | border-radius: @border-radius; |
|
736 | 742 | } |
|
737 | 743 | } |
|
738 | 744 | } |
|
739 | 745 | |
|
740 | 746 | /* Main comments*/ |
|
741 | 747 | #comments { |
|
742 | 748 | .comment-selected { |
|
743 | 749 | border-left: 6px solid @comment-highlight-color; |
|
744 | 750 | padding-left: 3px; |
|
745 | 751 | margin-left: -9px; |
|
746 | 752 | } |
|
747 | 753 | } |
|
748 | 754 | |
|
749 | 755 | .filediff { |
|
750 | 756 | border: 1px solid @grey5; |
|
751 | 757 | |
|
752 | 758 | /* START OVERRIDES */ |
|
753 | 759 | .code-highlight { |
|
754 | 760 | border: none; // TODO: remove this border from the global |
|
755 | 761 | // .code-highlight, it doesn't belong there |
|
756 | 762 | } |
|
757 | 763 | label { |
|
758 | 764 | margin: 0; // TODO: remove this margin definition from global label |
|
759 | 765 | // it doesn't belong there - if margin on labels |
|
760 | 766 | // are needed for a form they should be defined |
|
761 | 767 | // in the form's class |
|
762 | 768 | } |
|
763 | 769 | /* END OVERRIDES */ |
|
764 | 770 | |
|
765 | 771 | * { |
|
766 | 772 | box-sizing: border-box; |
|
767 | 773 | } |
|
768 | 774 | .filediff-anchor { |
|
769 | 775 | visibility: hidden; |
|
770 | 776 | } |
|
771 | 777 | &:hover { |
|
772 | 778 | .filediff-anchor { |
|
773 | 779 | visibility: visible; |
|
774 | 780 | } |
|
775 | 781 | } |
|
776 | 782 | |
|
777 | 783 | .filediff-collapse-indicator { |
|
778 | width: 0; | |
|
779 | height: 0; | |
|
780 | 784 | border-style: solid; |
|
781 | 785 | float: left; |
|
782 |
margin: |
|
|
786 | margin: 4px 0px 0 0; | |
|
783 | 787 | cursor: pointer; |
|
784 | 788 | } |
|
785 | 789 | |
|
786 | 790 | .filediff-heading { |
|
787 | 791 | background: @grey7; |
|
788 | 792 | cursor: pointer; |
|
789 | 793 | display: block; |
|
790 | 794 | padding: 5px 10px; |
|
791 | 795 | } |
|
792 | 796 | .filediff-heading:after { |
|
793 | 797 | content: ""; |
|
794 | 798 | display: table; |
|
795 | 799 | clear: both; |
|
796 | 800 | } |
|
797 | 801 | .filediff-heading:hover { |
|
798 | 802 | background: #e1e9f4 !important; |
|
799 | 803 | } |
|
800 | 804 | |
|
801 | 805 | .filediff-menu { |
|
802 | 806 | float: right; |
|
807 | text-align: right; | |
|
808 | padding: 5px 5px 5px 0px; | |
|
803 | 809 | |
|
804 |
&> a, |
|
|
805 |
|
|
|
806 |
|
|
|
807 | float: left | |
|
810 | &> a, | |
|
811 | &> span { | |
|
812 | padding: 1px; | |
|
808 | 813 | } |
|
809 | 814 | } |
|
810 | 815 | |
|
811 | 816 | .pill { |
|
812 | 817 | &[op="name"] { |
|
813 | 818 | background: none; |
|
814 | 819 | color: @grey2; |
|
815 | 820 | opacity: 1; |
|
816 | 821 | color: white; |
|
817 | 822 | } |
|
818 | 823 | &[op="limited"] { |
|
819 | 824 | background: @grey2; |
|
820 | 825 | color: white; |
|
821 | 826 | } |
|
822 | 827 | &[op="binary"] { |
|
823 | 828 | background: @color7; |
|
824 | 829 | color: white; |
|
825 | 830 | } |
|
826 | 831 | &[op="modified"] { |
|
827 | 832 | background: @alert1; |
|
828 | 833 | color: white; |
|
829 | 834 | } |
|
830 | 835 | &[op="renamed"] { |
|
831 | 836 | background: @color4; |
|
832 | 837 | color: white; |
|
833 | 838 | } |
|
834 | 839 | &[op="mode"] { |
|
835 | 840 | background: @grey3; |
|
836 | 841 | color: white; |
|
837 | 842 | } |
|
838 | 843 | &[op="symlink"] { |
|
839 | 844 | background: @color8; |
|
840 | 845 | color: white; |
|
841 | 846 | } |
|
842 | 847 | |
|
843 | 848 | &[op="added"] { /* added lines */ |
|
844 | 849 | background: @alert1; |
|
845 | 850 | color: white; |
|
846 | 851 | } |
|
847 | 852 | &[op="deleted"] { /* deleted lines */ |
|
848 | 853 | background: @alert2; |
|
849 | 854 | color: white; |
|
850 | 855 | } |
|
851 | 856 | |
|
852 | 857 | &[op="created"] { /* created file */ |
|
853 | 858 | background: @alert1; |
|
854 | 859 | color: white; |
|
855 | 860 | } |
|
856 | 861 | &[op="removed"] { /* deleted file */ |
|
857 | 862 | background: @color5; |
|
858 | 863 | color: white; |
|
859 | 864 | } |
|
860 | 865 | } |
|
861 | 866 | |
|
862 | 867 | .filediff-collapse-button, .filediff-expand-button { |
|
863 | 868 | cursor: pointer; |
|
864 | 869 | } |
|
865 | 870 | .filediff-collapse-button { |
|
866 | 871 | display: inline; |
|
867 | 872 | } |
|
868 | 873 | .filediff-expand-button { |
|
869 | 874 | display: none; |
|
870 | 875 | } |
|
871 | 876 | .filediff-collapsed .filediff-collapse-button { |
|
872 | 877 | display: none; |
|
873 | 878 | } |
|
874 | 879 | .filediff-collapsed .filediff-expand-button { |
|
875 | 880 | display: inline; |
|
876 | 881 | } |
|
877 | 882 | |
|
878 | 883 | @comment-padding: 5px; |
|
879 | 884 | |
|
880 | 885 | /**** COMMENTS ****/ |
|
881 | 886 | |
|
882 | 887 | .filediff-menu { |
|
883 | 888 | .show-comment-button { |
|
884 | 889 | display: none; |
|
885 | 890 | } |
|
886 | 891 | } |
|
887 | 892 | &.hide-comments { |
|
888 | 893 | .inline-comments { |
|
889 | 894 | display: none; |
|
890 | 895 | } |
|
891 | 896 | .filediff-menu { |
|
892 | 897 | .show-comment-button { |
|
893 | 898 | display: inline; |
|
894 | 899 | } |
|
895 | 900 | .hide-comment-button { |
|
896 | 901 | display: none; |
|
897 | 902 | } |
|
898 | 903 | } |
|
899 | 904 | } |
|
900 | 905 | |
|
901 | 906 | .hide-line-comments { |
|
902 | 907 | .inline-comments { |
|
903 | 908 | display: none; |
|
904 | 909 | } |
|
905 | 910 | } |
|
906 | 911 | |
|
907 | 912 | .inline-comments { |
|
908 | 913 | border-radius: @border-radius; |
|
909 | 914 | background: @grey6; |
|
910 | 915 | .comment { |
|
911 | 916 | margin: 0; |
|
912 | 917 | border-radius: @border-radius; |
|
913 | 918 | } |
|
914 | 919 | .comment-outdated { |
|
915 | 920 | opacity: 0.5; |
|
916 | 921 | } |
|
917 | 922 | |
|
918 | 923 | .comment-inline { |
|
919 | 924 | background: white; |
|
920 | 925 | padding: (@comment-padding + 3px) @comment-padding; |
|
921 | 926 | border: @comment-padding solid @grey6; |
|
922 | 927 | |
|
923 | 928 | .text { |
|
924 | 929 | border: none; |
|
925 | 930 | } |
|
926 | 931 | .meta { |
|
927 | 932 | border-bottom: 1px solid @grey6; |
|
928 | 933 | padding-bottom: 10px; |
|
929 | 934 | } |
|
930 | 935 | } |
|
931 | 936 | .comment-selected { |
|
932 | 937 | border-left: 6px solid @comment-highlight-color; |
|
933 | 938 | } |
|
934 | 939 | .comment-inline-form { |
|
935 | 940 | padding: @comment-padding; |
|
936 | 941 | display: none; |
|
937 | 942 | } |
|
938 | 943 | .cb-comment-add-button { |
|
939 | 944 | margin: @comment-padding; |
|
940 | 945 | } |
|
941 | 946 | /* hide add comment button when form is open */ |
|
942 | 947 | .comment-inline-form-open ~ .cb-comment-add-button { |
|
943 | 948 | display: none; |
|
944 | 949 | } |
|
945 | 950 | .comment-inline-form-open { |
|
946 | 951 | display: block; |
|
947 | 952 | } |
|
948 | 953 | /* hide add comment button when form but no comments */ |
|
949 | 954 | .comment-inline-form:first-child + .cb-comment-add-button { |
|
950 | 955 | display: none; |
|
951 | 956 | } |
|
952 | 957 | /* hide add comment button when no comments or form */ |
|
953 | 958 | .cb-comment-add-button:first-child { |
|
954 | 959 | display: none; |
|
955 | 960 | } |
|
956 | 961 | /* hide add comment button when only comment is being deleted */ |
|
957 | 962 | .comment-deleting:first-child + .cb-comment-add-button { |
|
958 | 963 | display: none; |
|
959 | 964 | } |
|
960 | 965 | } |
|
961 | 966 | /**** END COMMENTS ****/ |
|
962 | 967 | |
|
963 | 968 | } |
|
964 | 969 | |
|
970 | .filediff-outdated { | |
|
971 | padding: 8px 0; | |
|
972 | ||
|
973 | .filediff-heading { | |
|
974 | opacity: .5; | |
|
975 | } | |
|
976 | } | |
|
965 | 977 | |
|
966 | 978 | table.cb { |
|
967 | 979 | width: 100%; |
|
968 | 980 | border-collapse: collapse; |
|
969 | 981 | |
|
970 | 982 | .cb-text { |
|
971 | 983 | padding: @cb-text-padding; |
|
972 | 984 | } |
|
973 | 985 | .cb-hunk { |
|
974 | 986 | padding: @cb-text-padding; |
|
975 | 987 | } |
|
976 | 988 | .cb-expand { |
|
977 | 989 | display: none; |
|
978 | 990 | } |
|
979 | 991 | .cb-collapse { |
|
980 | 992 | display: inline; |
|
981 | 993 | } |
|
982 | 994 | &.cb-collapsed { |
|
983 | 995 | .cb-line { |
|
984 | 996 | display: none; |
|
985 | 997 | } |
|
986 | 998 | .cb-expand { |
|
987 | 999 | display: inline; |
|
988 | 1000 | } |
|
989 | 1001 | .cb-collapse { |
|
990 | 1002 | display: none; |
|
991 | 1003 | } |
|
992 | 1004 | } |
|
993 | 1005 | |
|
994 | 1006 | /* intentionally general selector since .cb-line-selected must override it |
|
995 | 1007 | and they both use !important since the td itself may have a random color |
|
996 | 1008 | generated by annotation blocks. TLDR: if you change it, make sure |
|
997 | 1009 | annotated block selection and line selection in file view still work */ |
|
998 | 1010 | .cb-line-fresh .cb-content { |
|
999 | 1011 | background: white !important; |
|
1000 | 1012 | } |
|
1001 | 1013 | .cb-warning { |
|
1002 | 1014 | background: #fff4dd; |
|
1003 | 1015 | } |
|
1004 | 1016 | |
|
1005 | 1017 | &.cb-diff-sideside { |
|
1006 | 1018 | td { |
|
1007 | 1019 | &.cb-content { |
|
1008 | 1020 | width: 50%; |
|
1009 | 1021 | } |
|
1010 | 1022 | } |
|
1011 | 1023 | } |
|
1012 | 1024 | |
|
1013 | 1025 | tr { |
|
1014 | 1026 | &.cb-annotate { |
|
1015 | 1027 | border-top: 1px solid #eee; |
|
1016 | 1028 | |
|
1017 | 1029 | &+ .cb-line { |
|
1018 | 1030 | border-top: 1px solid #eee; |
|
1019 | 1031 | } |
|
1020 | 1032 | |
|
1021 | 1033 | &:first-child { |
|
1022 | 1034 | border-top: none; |
|
1023 | 1035 | &+ .cb-line { |
|
1024 | 1036 | border-top: none; |
|
1025 | 1037 | } |
|
1026 | 1038 | } |
|
1027 | 1039 | } |
|
1028 | 1040 | |
|
1029 | 1041 | &.cb-hunk { |
|
1030 | 1042 | font-family: @font-family-monospace; |
|
1031 | 1043 | color: rgba(0, 0, 0, 0.3); |
|
1032 | 1044 | |
|
1033 | 1045 | td { |
|
1034 | 1046 | &:first-child { |
|
1035 | 1047 | background: #edf2f9; |
|
1036 | 1048 | } |
|
1037 | 1049 | &:last-child { |
|
1038 | 1050 | background: #f4f7fb; |
|
1039 | 1051 | } |
|
1040 | 1052 | } |
|
1041 | 1053 | } |
|
1042 | 1054 | } |
|
1043 | 1055 | |
|
1044 | 1056 | |
|
1045 | 1057 | td { |
|
1046 | 1058 | vertical-align: top; |
|
1047 | 1059 | padding: 0; |
|
1048 | 1060 | |
|
1049 | 1061 | &.cb-content { |
|
1050 | 1062 | font-size: 12.35px; |
|
1051 | 1063 | |
|
1052 | 1064 | &.cb-line-selected .cb-code { |
|
1053 | 1065 | background: @comment-highlight-color !important; |
|
1054 | 1066 | } |
|
1055 | 1067 | |
|
1056 | 1068 | span.cb-code { |
|
1057 | 1069 | line-height: @cb-line-height; |
|
1058 | 1070 | padding-left: @cb-line-code-padding; |
|
1059 | 1071 | padding-right: @cb-line-code-padding; |
|
1060 | 1072 | display: block; |
|
1061 | 1073 | white-space: pre-wrap; |
|
1062 | 1074 | font-family: @font-family-monospace; |
|
1063 | 1075 | word-break: break-word; |
|
1064 | 1076 | .nonl { |
|
1065 | 1077 | color: @color5; |
|
1066 | 1078 | } |
|
1067 | 1079 | } |
|
1068 | 1080 | |
|
1069 | 1081 | &> button.cb-comment-box-opener { |
|
1070 | 1082 | |
|
1071 |
padding: 2px |
|
|
1072 |
margin-left: |
|
|
1083 | padding: 2px 2px 1px 3px; | |
|
1084 | margin-left: -6px; | |
|
1073 | 1085 | margin-top: -1px; |
|
1074 | 1086 | |
|
1075 | 1087 | border-radius: @border-radius; |
|
1076 | 1088 | position: absolute; |
|
1077 | 1089 | display: none; |
|
1078 | 1090 | } |
|
1079 | 1091 | .cb-comment { |
|
1080 | 1092 | margin-top: 10px; |
|
1081 | 1093 | white-space: normal; |
|
1082 | 1094 | } |
|
1083 | 1095 | } |
|
1084 | 1096 | &:hover { |
|
1085 | 1097 | button.cb-comment-box-opener { |
|
1086 | 1098 | display: block; |
|
1087 | 1099 | } |
|
1088 | 1100 | &+ td button.cb-comment-box-opener { |
|
1089 | 1101 | display: block |
|
1090 | 1102 | } |
|
1091 | 1103 | } |
|
1092 | 1104 | |
|
1093 | 1105 | &.cb-data { |
|
1094 | 1106 | text-align: right; |
|
1095 | 1107 | width: 30px; |
|
1096 | 1108 | font-family: @font-family-monospace; |
|
1097 | 1109 | |
|
1098 | 1110 | .icon-comment { |
|
1099 | 1111 | cursor: pointer; |
|
1100 | 1112 | } |
|
1101 | 1113 | &.cb-line-selected > div { |
|
1102 | 1114 | display: block; |
|
1103 | 1115 | background: @comment-highlight-color !important; |
|
1104 | 1116 | line-height: @cb-line-height; |
|
1105 | 1117 | color: rgba(0, 0, 0, 0.3); |
|
1106 | 1118 | } |
|
1107 | 1119 | } |
|
1108 | 1120 | |
|
1109 | 1121 | &.cb-lineno { |
|
1110 | 1122 | padding: 0; |
|
1111 | 1123 | width: 50px; |
|
1112 | 1124 | color: rgba(0, 0, 0, 0.3); |
|
1113 | 1125 | text-align: right; |
|
1114 | 1126 | border-right: 1px solid #eee; |
|
1115 | 1127 | font-family: @font-family-monospace; |
|
1116 | 1128 | |
|
1117 | 1129 | a::before { |
|
1118 | 1130 | content: attr(data-line-no); |
|
1119 | 1131 | } |
|
1120 | 1132 | &.cb-line-selected a { |
|
1121 | 1133 | background: @comment-highlight-color !important; |
|
1122 | 1134 | } |
|
1123 | 1135 | |
|
1124 | 1136 | a { |
|
1125 | 1137 | display: block; |
|
1126 | 1138 | padding-right: @cb-line-code-padding; |
|
1127 | 1139 | padding-left: @cb-line-code-padding; |
|
1128 | 1140 | line-height: @cb-line-height; |
|
1129 | 1141 | color: rgba(0, 0, 0, 0.3); |
|
1130 | 1142 | } |
|
1131 | 1143 | } |
|
1132 | 1144 | |
|
1133 | 1145 | &.cb-empty { |
|
1134 | 1146 | background: @grey7; |
|
1135 | 1147 | } |
|
1136 | 1148 | |
|
1137 | 1149 | ins { |
|
1138 | 1150 | color: black; |
|
1139 | 1151 | background: #a6f3a6; |
|
1140 | 1152 | text-decoration: none; |
|
1141 | 1153 | } |
|
1142 | 1154 | del { |
|
1143 | 1155 | color: black; |
|
1144 | 1156 | background: #f8cbcb; |
|
1145 | 1157 | text-decoration: none; |
|
1146 | 1158 | } |
|
1147 | 1159 | &.cb-addition { |
|
1148 | 1160 | background: #ecffec; |
|
1149 | 1161 | |
|
1150 | 1162 | &.blob-lineno { |
|
1151 | 1163 | background: #ddffdd; |
|
1152 | 1164 | } |
|
1153 | 1165 | } |
|
1154 | 1166 | &.cb-deletion { |
|
1155 | 1167 | background: #ffecec; |
|
1156 | 1168 | |
|
1157 | 1169 | &.blob-lineno { |
|
1158 | 1170 | background: #ffdddd; |
|
1159 | 1171 | } |
|
1160 | 1172 | } |
|
1161 | 1173 | |
|
1162 | 1174 | &.cb-annotate-info { |
|
1163 | 1175 | width: 320px; |
|
1164 | 1176 | min-width: 320px; |
|
1165 | 1177 | max-width: 320px; |
|
1166 | 1178 | padding: 5px 2px; |
|
1167 | 1179 | font-size: 13px; |
|
1168 | 1180 | |
|
1169 | 1181 | strong.cb-annotate-message { |
|
1170 | 1182 | padding: 5px 0; |
|
1171 | 1183 | white-space: pre-line; |
|
1172 | 1184 | display: inline-block; |
|
1173 | 1185 | } |
|
1174 | 1186 | .rc-user { |
|
1175 | 1187 | float: none; |
|
1176 | 1188 | padding: 0 6px 0 17px; |
|
1177 | 1189 | min-width: auto; |
|
1178 | 1190 | min-height: auto; |
|
1179 | 1191 | } |
|
1180 | 1192 | } |
|
1181 | 1193 | |
|
1182 | 1194 | &.cb-annotate-revision { |
|
1183 | 1195 | cursor: pointer; |
|
1184 | 1196 | text-align: right; |
|
1185 | 1197 | } |
|
1186 | 1198 | } |
|
1187 | 1199 | } |
@@ -1,2217 +1,2223 b'' | |||
|
1 | 1 | //Primary CSS |
|
2 | 2 | |
|
3 | 3 | //--- IMPORTS ------------------// |
|
4 | 4 | |
|
5 | 5 | @import 'helpers'; |
|
6 | 6 | @import 'mixins'; |
|
7 | 7 | @import 'rcicons'; |
|
8 | 8 | @import 'fonts'; |
|
9 | 9 | @import 'variables'; |
|
10 | 10 | @import 'bootstrap-variables'; |
|
11 | 11 | @import 'form-bootstrap'; |
|
12 | 12 | @import 'codemirror'; |
|
13 | 13 | @import 'legacy_code_styles'; |
|
14 | 14 | @import 'progress-bar'; |
|
15 | 15 | |
|
16 | 16 | @import 'type'; |
|
17 | 17 | @import 'alerts'; |
|
18 | 18 | @import 'buttons'; |
|
19 | 19 | @import 'tags'; |
|
20 | 20 | @import 'code-block'; |
|
21 | 21 | @import 'examples'; |
|
22 | 22 | @import 'login'; |
|
23 | 23 | @import 'main-content'; |
|
24 | 24 | @import 'select2'; |
|
25 | 25 | @import 'comments'; |
|
26 | 26 | @import 'panels-bootstrap'; |
|
27 | 27 | @import 'panels'; |
|
28 | 28 | @import 'deform'; |
|
29 | 29 | |
|
30 | 30 | //--- BASE ------------------// |
|
31 | 31 | .noscript-error { |
|
32 | 32 | top: 0; |
|
33 | 33 | left: 0; |
|
34 | 34 | width: 100%; |
|
35 | 35 | z-index: 101; |
|
36 | 36 | text-align: center; |
|
37 | 37 | font-family: @text-semibold; |
|
38 | 38 | font-size: 120%; |
|
39 | 39 | color: white; |
|
40 | 40 | background-color: @alert2; |
|
41 | 41 | padding: 5px 0 5px 0; |
|
42 | 42 | } |
|
43 | 43 | |
|
44 | 44 | html { |
|
45 | 45 | display: table; |
|
46 | 46 | height: 100%; |
|
47 | 47 | width: 100%; |
|
48 | 48 | } |
|
49 | 49 | |
|
50 | 50 | body { |
|
51 | 51 | display: table-cell; |
|
52 | 52 | width: 100%; |
|
53 | 53 | } |
|
54 | 54 | |
|
55 | 55 | //--- LAYOUT ------------------// |
|
56 | 56 | |
|
57 | 57 | .hidden{ |
|
58 | 58 | display: none !important; |
|
59 | 59 | } |
|
60 | 60 | |
|
61 | 61 | .box{ |
|
62 | 62 | float: left; |
|
63 | 63 | width: 100%; |
|
64 | 64 | } |
|
65 | 65 | |
|
66 | 66 | .browser-header { |
|
67 | 67 | clear: both; |
|
68 | 68 | } |
|
69 | 69 | .main { |
|
70 | 70 | clear: both; |
|
71 | 71 | padding:0 0 @pagepadding; |
|
72 | 72 | height: auto; |
|
73 | 73 | |
|
74 | 74 | &:after { //clearfix |
|
75 | 75 | content:""; |
|
76 | 76 | clear:both; |
|
77 | 77 | width:100%; |
|
78 | 78 | display:block; |
|
79 | 79 | } |
|
80 | 80 | } |
|
81 | 81 | |
|
82 | 82 | .action-link{ |
|
83 | 83 | margin-left: @padding; |
|
84 | 84 | padding-left: @padding; |
|
85 | 85 | border-left: @border-thickness solid @border-default-color; |
|
86 | 86 | } |
|
87 | 87 | |
|
88 | 88 | input + .action-link, .action-link.first{ |
|
89 | 89 | border-left: none; |
|
90 | 90 | } |
|
91 | 91 | |
|
92 | 92 | .action-link.last{ |
|
93 | 93 | margin-right: @padding; |
|
94 | 94 | padding-right: @padding; |
|
95 | 95 | } |
|
96 | 96 | |
|
97 | 97 | .action-link.active, |
|
98 | 98 | .action-link.active a{ |
|
99 | 99 | color: @grey4; |
|
100 | 100 | } |
|
101 | 101 | |
|
102 | 102 | ul.simple-list{ |
|
103 | 103 | list-style: none; |
|
104 | 104 | margin: 0; |
|
105 | 105 | padding: 0; |
|
106 | 106 | } |
|
107 | 107 | |
|
108 | 108 | .main-content { |
|
109 | 109 | padding-bottom: @pagepadding; |
|
110 | 110 | } |
|
111 | 111 | |
|
112 | 112 | .wide-mode-wrapper { |
|
113 |
max-width: |
|
|
113 | max-width:4000px !important; | |
|
114 | 114 | } |
|
115 | 115 | |
|
116 | 116 | .wrapper { |
|
117 | 117 | position: relative; |
|
118 | 118 | max-width: @wrapper-maxwidth; |
|
119 | 119 | margin: 0 auto; |
|
120 | 120 | } |
|
121 | 121 | |
|
122 | 122 | #content { |
|
123 | 123 | clear: both; |
|
124 | 124 | padding: 0 @contentpadding; |
|
125 | 125 | } |
|
126 | 126 | |
|
127 | 127 | .advanced-settings-fields{ |
|
128 | 128 | input{ |
|
129 | 129 | margin-left: @textmargin; |
|
130 | 130 | margin-right: @padding/2; |
|
131 | 131 | } |
|
132 | 132 | } |
|
133 | 133 | |
|
134 | 134 | .cs_files_title { |
|
135 | 135 | margin: @pagepadding 0 0; |
|
136 | 136 | } |
|
137 | 137 | |
|
138 | 138 | input.inline[type="file"] { |
|
139 | 139 | display: inline; |
|
140 | 140 | } |
|
141 | 141 | |
|
142 | 142 | .error_page { |
|
143 | 143 | margin: 10% auto; |
|
144 | 144 | |
|
145 | 145 | h1 { |
|
146 | 146 | color: @grey2; |
|
147 | 147 | } |
|
148 | 148 | |
|
149 | 149 | .alert { |
|
150 | 150 | margin: @padding 0; |
|
151 | 151 | } |
|
152 | 152 | |
|
153 | 153 | .error-branding { |
|
154 | 154 | font-family: @text-semibold; |
|
155 | 155 | color: @grey4; |
|
156 | 156 | } |
|
157 | 157 | |
|
158 | 158 | .error_message { |
|
159 | 159 | font-family: @text-regular; |
|
160 | 160 | } |
|
161 | 161 | |
|
162 | 162 | .sidebar { |
|
163 | 163 | min-height: 275px; |
|
164 | 164 | margin: 0; |
|
165 | 165 | padding: 0 0 @sidebarpadding @sidebarpadding; |
|
166 | 166 | border: none; |
|
167 | 167 | } |
|
168 | 168 | |
|
169 | 169 | .main-content { |
|
170 | 170 | position: relative; |
|
171 | 171 | margin: 0 @sidebarpadding @sidebarpadding; |
|
172 | 172 | padding: 0 0 0 @sidebarpadding; |
|
173 | 173 | border-left: @border-thickness solid @grey5; |
|
174 | 174 | |
|
175 | 175 | @media (max-width:767px) { |
|
176 | 176 | clear: both; |
|
177 | 177 | width: 100%; |
|
178 | 178 | margin: 0; |
|
179 | 179 | border: none; |
|
180 | 180 | } |
|
181 | 181 | } |
|
182 | 182 | |
|
183 | 183 | .inner-column { |
|
184 | 184 | float: left; |
|
185 | 185 | width: 29.75%; |
|
186 | 186 | min-height: 150px; |
|
187 | 187 | margin: @sidebarpadding 2% 0 0; |
|
188 | 188 | padding: 0 2% 0 0; |
|
189 | 189 | border-right: @border-thickness solid @grey5; |
|
190 | 190 | |
|
191 | 191 | @media (max-width:767px) { |
|
192 | 192 | clear: both; |
|
193 | 193 | width: 100%; |
|
194 | 194 | border: none; |
|
195 | 195 | } |
|
196 | 196 | |
|
197 | 197 | ul { |
|
198 | 198 | padding-left: 1.25em; |
|
199 | 199 | } |
|
200 | 200 | |
|
201 | 201 | &:last-child { |
|
202 | 202 | margin: @sidebarpadding 0 0; |
|
203 | 203 | border: none; |
|
204 | 204 | } |
|
205 | 205 | |
|
206 | 206 | h4 { |
|
207 | 207 | margin: 0 0 @padding; |
|
208 | 208 | font-family: @text-semibold; |
|
209 | 209 | } |
|
210 | 210 | } |
|
211 | 211 | } |
|
212 | 212 | .error-page-logo { |
|
213 | 213 | width: 130px; |
|
214 | 214 | height: 160px; |
|
215 | 215 | } |
|
216 | 216 | |
|
217 | 217 | // HEADER |
|
218 | 218 | .header { |
|
219 | 219 | |
|
220 | 220 | // TODO: johbo: Fix login pages, so that they work without a min-height |
|
221 | 221 | // for the header and then remove the min-height. I chose a smaller value |
|
222 | 222 | // intentionally here to avoid rendering issues in the main navigation. |
|
223 | 223 | min-height: 49px; |
|
224 | 224 | |
|
225 | 225 | position: relative; |
|
226 | 226 | vertical-align: bottom; |
|
227 | 227 | padding: 0 @header-padding; |
|
228 | 228 | background-color: @grey2; |
|
229 | 229 | color: @grey5; |
|
230 | 230 | |
|
231 | 231 | .title { |
|
232 | 232 | overflow: visible; |
|
233 | 233 | } |
|
234 | 234 | |
|
235 | 235 | &:before, |
|
236 | 236 | &:after { |
|
237 | 237 | content: ""; |
|
238 | 238 | clear: both; |
|
239 | 239 | width: 100%; |
|
240 | 240 | } |
|
241 | 241 | |
|
242 | 242 | // TODO: johbo: Avoids breaking "Repositories" chooser |
|
243 | 243 | .select2-container .select2-choice .select2-arrow { |
|
244 | 244 | display: none; |
|
245 | 245 | } |
|
246 | 246 | } |
|
247 | 247 | |
|
248 | 248 | #header-inner { |
|
249 | 249 | &.title { |
|
250 | 250 | margin: 0; |
|
251 | 251 | } |
|
252 | 252 | &:before, |
|
253 | 253 | &:after { |
|
254 | 254 | content: ""; |
|
255 | 255 | clear: both; |
|
256 | 256 | } |
|
257 | 257 | } |
|
258 | 258 | |
|
259 | 259 | // Gists |
|
260 | 260 | #files_data { |
|
261 | 261 | clear: both; //for firefox |
|
262 | 262 | } |
|
263 | 263 | #gistid { |
|
264 | 264 | margin-right: @padding; |
|
265 | 265 | } |
|
266 | 266 | |
|
267 | 267 | // Global Settings Editor |
|
268 | 268 | .textarea.editor { |
|
269 | 269 | float: left; |
|
270 | 270 | position: relative; |
|
271 | 271 | max-width: @texteditor-width; |
|
272 | 272 | |
|
273 | 273 | select { |
|
274 | 274 | position: absolute; |
|
275 | 275 | top:10px; |
|
276 | 276 | right:0; |
|
277 | 277 | } |
|
278 | 278 | |
|
279 | 279 | .CodeMirror { |
|
280 | 280 | margin: 0; |
|
281 | 281 | } |
|
282 | 282 | |
|
283 | 283 | .help-block { |
|
284 | 284 | margin: 0 0 @padding; |
|
285 | 285 | padding:.5em; |
|
286 | 286 | background-color: @grey6; |
|
287 | 287 | } |
|
288 | 288 | } |
|
289 | 289 | |
|
290 | 290 | ul.auth_plugins { |
|
291 | 291 | margin: @padding 0 @padding @legend-width; |
|
292 | 292 | padding: 0; |
|
293 | 293 | |
|
294 | 294 | li { |
|
295 | 295 | margin-bottom: @padding; |
|
296 | 296 | line-height: 1em; |
|
297 | 297 | list-style-type: none; |
|
298 | 298 | |
|
299 | 299 | .auth_buttons .btn { |
|
300 | 300 | margin-right: @padding; |
|
301 | 301 | } |
|
302 | 302 | |
|
303 | 303 | &:before { content: none; } |
|
304 | 304 | } |
|
305 | 305 | } |
|
306 | 306 | |
|
307 | 307 | |
|
308 | 308 | // My Account PR list |
|
309 | 309 | |
|
310 | 310 | #show_closed { |
|
311 | 311 | margin: 0 1em 0 0; |
|
312 | 312 | } |
|
313 | 313 | |
|
314 | 314 | .pullrequestlist { |
|
315 | 315 | .closed { |
|
316 | 316 | background-color: @grey6; |
|
317 | 317 | } |
|
318 | 318 | .td-status { |
|
319 | 319 | padding-left: .5em; |
|
320 | 320 | } |
|
321 | 321 | .log-container .truncate { |
|
322 | 322 | height: 2.75em; |
|
323 | 323 | white-space: pre-line; |
|
324 | 324 | } |
|
325 | 325 | table.rctable .user { |
|
326 | 326 | padding-left: 0; |
|
327 | 327 | } |
|
328 | 328 | table.rctable { |
|
329 | 329 | td.td-description, |
|
330 | 330 | .rc-user { |
|
331 | 331 | min-width: auto; |
|
332 | 332 | } |
|
333 | 333 | } |
|
334 | 334 | } |
|
335 | 335 | |
|
336 | 336 | // Pull Requests |
|
337 | 337 | |
|
338 | 338 | .pullrequests_section_head { |
|
339 | 339 | display: block; |
|
340 | 340 | clear: both; |
|
341 | 341 | margin: @padding 0; |
|
342 | 342 | font-family: @text-bold; |
|
343 | 343 | } |
|
344 | 344 | |
|
345 | 345 | .pr-origininfo, .pr-targetinfo { |
|
346 | 346 | position: relative; |
|
347 | 347 | |
|
348 | 348 | .tag { |
|
349 | 349 | display: inline-block; |
|
350 | 350 | margin: 0 1em .5em 0; |
|
351 | 351 | } |
|
352 | 352 | |
|
353 | 353 | .clone-url { |
|
354 | 354 | display: inline-block; |
|
355 | 355 | margin: 0 0 .5em 0; |
|
356 | 356 | padding: 0; |
|
357 | 357 | line-height: 1.2em; |
|
358 | 358 | } |
|
359 | 359 | } |
|
360 | 360 | |
|
361 | 361 | .pr-pullinfo { |
|
362 | 362 | clear: both; |
|
363 | 363 | margin: .5em 0; |
|
364 | 364 | } |
|
365 | 365 | |
|
366 | 366 | #pr-title-input { |
|
367 | 367 | width: 72%; |
|
368 | 368 | font-size: 1em; |
|
369 | 369 | font-family: @text-bold; |
|
370 | 370 | margin: 0; |
|
371 | 371 | padding: 0 0 0 @padding/4; |
|
372 | 372 | line-height: 1.7em; |
|
373 | 373 | color: @text-color; |
|
374 | 374 | letter-spacing: .02em; |
|
375 | 375 | } |
|
376 | 376 | |
|
377 | 377 | #pullrequest_title { |
|
378 | 378 | width: 100%; |
|
379 | 379 | box-sizing: border-box; |
|
380 | 380 | } |
|
381 | 381 | |
|
382 | 382 | #pr_open_message { |
|
383 | 383 | border: @border-thickness solid #fff; |
|
384 | 384 | border-radius: @border-radius; |
|
385 | 385 | padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0; |
|
386 | 386 | text-align: right; |
|
387 | 387 | overflow: hidden; |
|
388 | 388 | } |
|
389 | 389 | |
|
390 | 390 | .pr-submit-button { |
|
391 | 391 | float: right; |
|
392 | 392 | margin: 0 0 0 5px; |
|
393 | 393 | } |
|
394 | 394 | |
|
395 | 395 | .pr-spacing-container { |
|
396 | 396 | padding: 20px; |
|
397 | 397 | clear: both |
|
398 | 398 | } |
|
399 | 399 | |
|
400 | 400 | #pr-description-input { |
|
401 | 401 | margin-bottom: 0; |
|
402 | 402 | } |
|
403 | 403 | |
|
404 | 404 | .pr-description-label { |
|
405 | 405 | vertical-align: top; |
|
406 | 406 | } |
|
407 | 407 | |
|
408 | 408 | .perms_section_head { |
|
409 | 409 | min-width: 625px; |
|
410 | 410 | |
|
411 | 411 | h2 { |
|
412 | 412 | margin-bottom: 0; |
|
413 | 413 | } |
|
414 | 414 | |
|
415 | 415 | .label-checkbox { |
|
416 | 416 | float: left; |
|
417 | 417 | } |
|
418 | 418 | |
|
419 | 419 | &.field { |
|
420 | 420 | margin: @space 0 @padding; |
|
421 | 421 | } |
|
422 | 422 | |
|
423 | 423 | &:first-child.field { |
|
424 | 424 | margin-top: 0; |
|
425 | 425 | |
|
426 | 426 | .label { |
|
427 | 427 | margin-top: 0; |
|
428 | 428 | padding-top: 0; |
|
429 | 429 | } |
|
430 | 430 | |
|
431 | 431 | .radios { |
|
432 | 432 | padding-top: 0; |
|
433 | 433 | } |
|
434 | 434 | } |
|
435 | 435 | |
|
436 | 436 | .radios { |
|
437 | 437 | float: right; |
|
438 | 438 | position: relative; |
|
439 | 439 | width: 405px; |
|
440 | 440 | } |
|
441 | 441 | } |
|
442 | 442 | |
|
443 | 443 | //--- MODULES ------------------// |
|
444 | 444 | |
|
445 | 445 | |
|
446 | 446 | // Server Announcement |
|
447 | 447 | #server-announcement { |
|
448 | 448 | width: 95%; |
|
449 | 449 | margin: @padding auto; |
|
450 | 450 | padding: @padding; |
|
451 | 451 | border-width: 2px; |
|
452 | 452 | border-style: solid; |
|
453 | 453 | .border-radius(2px); |
|
454 | 454 | font-family: @text-bold; |
|
455 | 455 | |
|
456 | 456 | &.info { border-color: @alert4; background-color: @alert4-inner; } |
|
457 | 457 | &.warning { border-color: @alert3; background-color: @alert3-inner; } |
|
458 | 458 | &.error { border-color: @alert2; background-color: @alert2-inner; } |
|
459 | 459 | &.success { border-color: @alert1; background-color: @alert1-inner; } |
|
460 | 460 | &.neutral { border-color: @grey3; background-color: @grey6; } |
|
461 | 461 | } |
|
462 | 462 | |
|
463 | 463 | // Fixed Sidebar Column |
|
464 | 464 | .sidebar-col-wrapper { |
|
465 | 465 | padding-left: @sidebar-all-width; |
|
466 | 466 | |
|
467 | 467 | .sidebar { |
|
468 | 468 | width: @sidebar-width; |
|
469 | 469 | margin-left: -@sidebar-all-width; |
|
470 | 470 | } |
|
471 | 471 | } |
|
472 | 472 | |
|
473 | 473 | .sidebar-col-wrapper.scw-small { |
|
474 | 474 | padding-left: @sidebar-small-all-width; |
|
475 | 475 | |
|
476 | 476 | .sidebar { |
|
477 | 477 | width: @sidebar-small-width; |
|
478 | 478 | margin-left: -@sidebar-small-all-width; |
|
479 | 479 | } |
|
480 | 480 | } |
|
481 | 481 | |
|
482 | 482 | |
|
483 | 483 | // FOOTER |
|
484 | 484 | #footer { |
|
485 | 485 | padding: 0; |
|
486 | 486 | text-align: center; |
|
487 | 487 | vertical-align: middle; |
|
488 | 488 | color: @grey2; |
|
489 | 489 | background-color: @grey6; |
|
490 | 490 | |
|
491 | 491 | p { |
|
492 | 492 | margin: 0; |
|
493 | 493 | padding: 1em; |
|
494 | 494 | line-height: 1em; |
|
495 | 495 | } |
|
496 | 496 | |
|
497 | 497 | .server-instance { //server instance |
|
498 | 498 | display: none; |
|
499 | 499 | } |
|
500 | 500 | |
|
501 | 501 | .title { |
|
502 | 502 | float: none; |
|
503 | 503 | margin: 0 auto; |
|
504 | 504 | } |
|
505 | 505 | } |
|
506 | 506 | |
|
507 | 507 | button.close { |
|
508 | 508 | padding: 0; |
|
509 | 509 | cursor: pointer; |
|
510 | 510 | background: transparent; |
|
511 | 511 | border: 0; |
|
512 | 512 | .box-shadow(none); |
|
513 | 513 | -webkit-appearance: none; |
|
514 | 514 | } |
|
515 | 515 | |
|
516 | 516 | .close { |
|
517 | 517 | float: right; |
|
518 | 518 | font-size: 21px; |
|
519 | 519 | font-family: @text-bootstrap; |
|
520 | 520 | line-height: 1em; |
|
521 | 521 | font-weight: bold; |
|
522 | 522 | color: @grey2; |
|
523 | 523 | |
|
524 | 524 | &:hover, |
|
525 | 525 | &:focus { |
|
526 | 526 | color: @grey1; |
|
527 | 527 | text-decoration: none; |
|
528 | 528 | cursor: pointer; |
|
529 | 529 | } |
|
530 | 530 | } |
|
531 | 531 | |
|
532 | 532 | // GRID |
|
533 | 533 | .sorting, |
|
534 | 534 | .sorting_desc, |
|
535 | 535 | .sorting_asc { |
|
536 | 536 | cursor: pointer; |
|
537 | 537 | } |
|
538 | 538 | .sorting_desc:after { |
|
539 | 539 | content: "\00A0\25B2"; |
|
540 | 540 | font-size: .75em; |
|
541 | 541 | } |
|
542 | 542 | .sorting_asc:after { |
|
543 | 543 | content: "\00A0\25BC"; |
|
544 | 544 | font-size: .68em; |
|
545 | 545 | } |
|
546 | 546 | |
|
547 | 547 | |
|
548 | 548 | .user_auth_tokens { |
|
549 | 549 | |
|
550 | 550 | &.truncate { |
|
551 | 551 | white-space: nowrap; |
|
552 | 552 | overflow: hidden; |
|
553 | 553 | text-overflow: ellipsis; |
|
554 | 554 | } |
|
555 | 555 | |
|
556 | 556 | .fields .field .input { |
|
557 | 557 | margin: 0; |
|
558 | 558 | } |
|
559 | 559 | |
|
560 | 560 | input#description { |
|
561 | 561 | width: 100px; |
|
562 | 562 | margin: 0; |
|
563 | 563 | } |
|
564 | 564 | |
|
565 | 565 | .drop-menu { |
|
566 | 566 | // TODO: johbo: Remove this, should work out of the box when |
|
567 | 567 | // having multiple inputs inline |
|
568 | 568 | margin: 0 0 0 5px; |
|
569 | 569 | } |
|
570 | 570 | } |
|
571 | 571 | #user_list_table { |
|
572 | 572 | .closed { |
|
573 | 573 | background-color: @grey6; |
|
574 | 574 | } |
|
575 | 575 | } |
|
576 | 576 | |
|
577 | 577 | |
|
578 | 578 | input { |
|
579 | 579 | &.disabled { |
|
580 | 580 | opacity: .5; |
|
581 | 581 | } |
|
582 | 582 | } |
|
583 | 583 | |
|
584 | 584 | // remove extra padding in firefox |
|
585 | 585 | input::-moz-focus-inner { border:0; padding:0 } |
|
586 | 586 | |
|
587 | 587 | .adjacent input { |
|
588 | 588 | margin-bottom: @padding; |
|
589 | 589 | } |
|
590 | 590 | |
|
591 | 591 | .permissions_boxes { |
|
592 | 592 | display: block; |
|
593 | 593 | } |
|
594 | 594 | |
|
595 | 595 | //TODO: lisa: this should be in tables |
|
596 | 596 | .show_more_col { |
|
597 | 597 | width: 20px; |
|
598 | 598 | } |
|
599 | 599 | |
|
600 | 600 | //FORMS |
|
601 | 601 | |
|
602 | 602 | .medium-inline, |
|
603 | 603 | input#description.medium-inline { |
|
604 | 604 | display: inline; |
|
605 | 605 | width: @medium-inline-input-width; |
|
606 | 606 | min-width: 100px; |
|
607 | 607 | } |
|
608 | 608 | |
|
609 | 609 | select { |
|
610 | 610 | //reset |
|
611 | 611 | -webkit-appearance: none; |
|
612 | 612 | -moz-appearance: none; |
|
613 | 613 | |
|
614 | 614 | display: inline-block; |
|
615 | 615 | height: 28px; |
|
616 | 616 | width: auto; |
|
617 | 617 | margin: 0 @padding @padding 0; |
|
618 | 618 | padding: 0 18px 0 8px; |
|
619 | 619 | line-height:1em; |
|
620 | 620 | font-size: @basefontsize; |
|
621 | 621 | border: @border-thickness solid @rcblue; |
|
622 | 622 | background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%; |
|
623 | 623 | color: @rcblue; |
|
624 | 624 | |
|
625 | 625 | &:after { |
|
626 | 626 | content: "\00A0\25BE"; |
|
627 | 627 | } |
|
628 | 628 | |
|
629 | 629 | &:focus { |
|
630 | 630 | outline: none; |
|
631 | 631 | } |
|
632 | 632 | } |
|
633 | 633 | |
|
634 | 634 | option { |
|
635 | 635 | &:focus { |
|
636 | 636 | outline: none; |
|
637 | 637 | } |
|
638 | 638 | } |
|
639 | 639 | |
|
640 | 640 | input, |
|
641 | 641 | textarea { |
|
642 | 642 | padding: @input-padding; |
|
643 | 643 | border: @input-border-thickness solid @border-highlight-color; |
|
644 | 644 | .border-radius (@border-radius); |
|
645 | 645 | font-family: @text-light; |
|
646 | 646 | font-size: @basefontsize; |
|
647 | 647 | |
|
648 | 648 | &.input-sm { |
|
649 | 649 | padding: 5px; |
|
650 | 650 | } |
|
651 | 651 | |
|
652 | 652 | &#description { |
|
653 | 653 | min-width: @input-description-minwidth; |
|
654 | 654 | min-height: 1em; |
|
655 | 655 | padding: 10px; |
|
656 | 656 | } |
|
657 | 657 | } |
|
658 | 658 | |
|
659 | 659 | .field-sm { |
|
660 | 660 | input, |
|
661 | 661 | textarea { |
|
662 | 662 | padding: 5px; |
|
663 | 663 | } |
|
664 | 664 | } |
|
665 | 665 | |
|
666 | 666 | textarea { |
|
667 | 667 | display: block; |
|
668 | 668 | clear: both; |
|
669 | 669 | width: 100%; |
|
670 | 670 | min-height: 100px; |
|
671 | 671 | margin-bottom: @padding; |
|
672 | 672 | .box-sizing(border-box); |
|
673 | 673 | overflow: auto; |
|
674 | 674 | } |
|
675 | 675 | |
|
676 | 676 | label { |
|
677 | 677 | font-family: @text-light; |
|
678 | 678 | } |
|
679 | 679 | |
|
680 | 680 | // GRAVATARS |
|
681 | 681 | // centers gravatar on username to the right |
|
682 | 682 | |
|
683 | 683 | .gravatar { |
|
684 | 684 | display: inline; |
|
685 | 685 | min-width: 16px; |
|
686 | 686 | min-height: 16px; |
|
687 | 687 | margin: -5px 0; |
|
688 | 688 | padding: 0; |
|
689 | 689 | line-height: 1em; |
|
690 | 690 | border: 1px solid @grey4; |
|
691 | 691 | |
|
692 | 692 | &.gravatar-large { |
|
693 | 693 | margin: -0.5em .25em -0.5em 0; |
|
694 | 694 | } |
|
695 | 695 | |
|
696 | 696 | & + .user { |
|
697 | 697 | display: inline; |
|
698 | 698 | margin: 0; |
|
699 | 699 | padding: 0 0 0 .17em; |
|
700 | 700 | line-height: 1em; |
|
701 | 701 | } |
|
702 | 702 | } |
|
703 | 703 | |
|
704 | 704 | .user-inline-data { |
|
705 | 705 | display: inline-block; |
|
706 | 706 | float: left; |
|
707 | 707 | padding-left: .5em; |
|
708 | 708 | line-height: 1.3em; |
|
709 | 709 | } |
|
710 | 710 | |
|
711 | 711 | .rc-user { // gravatar + user wrapper |
|
712 | 712 | float: left; |
|
713 | 713 | position: relative; |
|
714 | 714 | min-width: 100px; |
|
715 | 715 | max-width: 200px; |
|
716 | 716 | min-height: (@gravatar-size + @border-thickness * 2); // account for border |
|
717 | 717 | display: block; |
|
718 | 718 | padding: 0 0 0 (@gravatar-size + @basefontsize/2 + @border-thickness * 2); |
|
719 | 719 | |
|
720 | 720 | |
|
721 | 721 | .gravatar { |
|
722 | 722 | display: block; |
|
723 | 723 | position: absolute; |
|
724 | 724 | top: 0; |
|
725 | 725 | left: 0; |
|
726 | 726 | min-width: @gravatar-size; |
|
727 | 727 | min-height: @gravatar-size; |
|
728 | 728 | margin: 0; |
|
729 | 729 | } |
|
730 | 730 | |
|
731 | 731 | .user { |
|
732 | 732 | display: block; |
|
733 | 733 | max-width: 175px; |
|
734 | 734 | padding-top: 2px; |
|
735 | 735 | overflow: hidden; |
|
736 | 736 | text-overflow: ellipsis; |
|
737 | 737 | } |
|
738 | 738 | } |
|
739 | 739 | |
|
740 | 740 | .gist-gravatar, |
|
741 | 741 | .journal_container { |
|
742 | 742 | .gravatar-large { |
|
743 | 743 | margin: 0 .5em -10px 0; |
|
744 | 744 | } |
|
745 | 745 | } |
|
746 | 746 | |
|
747 | 747 | |
|
748 | 748 | // ADMIN SETTINGS |
|
749 | 749 | |
|
750 | 750 | // Tag Patterns |
|
751 | 751 | .tag_patterns { |
|
752 | 752 | .tag_input { |
|
753 | 753 | margin-bottom: @padding; |
|
754 | 754 | } |
|
755 | 755 | } |
|
756 | 756 | |
|
757 | 757 | .locked_input { |
|
758 | 758 | position: relative; |
|
759 | 759 | |
|
760 | 760 | input { |
|
761 | 761 | display: inline; |
|
762 | 762 | margin-top: 3px; |
|
763 | 763 | } |
|
764 | 764 | |
|
765 | 765 | br { |
|
766 | 766 | display: none; |
|
767 | 767 | } |
|
768 | 768 | |
|
769 | 769 | .error-message { |
|
770 | 770 | float: left; |
|
771 | 771 | width: 100%; |
|
772 | 772 | } |
|
773 | 773 | |
|
774 | 774 | .lock_input_button { |
|
775 | 775 | display: inline; |
|
776 | 776 | } |
|
777 | 777 | |
|
778 | 778 | .help-block { |
|
779 | 779 | clear: both; |
|
780 | 780 | } |
|
781 | 781 | } |
|
782 | 782 | |
|
783 | 783 | // Notifications |
|
784 | 784 | |
|
785 | 785 | .notifications_buttons { |
|
786 | 786 | margin: 0 0 @space 0; |
|
787 | 787 | padding: 0; |
|
788 | 788 | |
|
789 | 789 | .btn { |
|
790 | 790 | display: inline-block; |
|
791 | 791 | } |
|
792 | 792 | } |
|
793 | 793 | |
|
794 | 794 | .notification-list { |
|
795 | 795 | |
|
796 | 796 | div { |
|
797 | 797 | display: inline-block; |
|
798 | 798 | vertical-align: middle; |
|
799 | 799 | } |
|
800 | 800 | |
|
801 | 801 | .container { |
|
802 | 802 | display: block; |
|
803 | 803 | margin: 0 0 @padding 0; |
|
804 | 804 | } |
|
805 | 805 | |
|
806 | 806 | .delete-notifications { |
|
807 | 807 | margin-left: @padding; |
|
808 | 808 | text-align: right; |
|
809 | 809 | cursor: pointer; |
|
810 | 810 | } |
|
811 | 811 | |
|
812 | 812 | .read-notifications { |
|
813 | 813 | margin-left: @padding/2; |
|
814 | 814 | text-align: right; |
|
815 | 815 | width: 35px; |
|
816 | 816 | cursor: pointer; |
|
817 | 817 | } |
|
818 | 818 | |
|
819 | 819 | .icon-minus-sign { |
|
820 | 820 | color: @alert2; |
|
821 | 821 | } |
|
822 | 822 | |
|
823 | 823 | .icon-ok-sign { |
|
824 | 824 | color: @alert1; |
|
825 | 825 | } |
|
826 | 826 | } |
|
827 | 827 | |
|
828 | 828 | .user_settings { |
|
829 | 829 | float: left; |
|
830 | 830 | clear: both; |
|
831 | 831 | display: block; |
|
832 | 832 | width: 100%; |
|
833 | 833 | |
|
834 | 834 | .gravatar_box { |
|
835 | 835 | margin-bottom: @padding; |
|
836 | 836 | |
|
837 | 837 | &:after { |
|
838 | 838 | content: " "; |
|
839 | 839 | clear: both; |
|
840 | 840 | width: 100%; |
|
841 | 841 | } |
|
842 | 842 | } |
|
843 | 843 | |
|
844 | 844 | .fields .field { |
|
845 | 845 | clear: both; |
|
846 | 846 | } |
|
847 | 847 | } |
|
848 | 848 | |
|
849 | 849 | .advanced_settings { |
|
850 | 850 | margin-bottom: @space; |
|
851 | 851 | |
|
852 | 852 | .help-block { |
|
853 | 853 | margin-left: 0; |
|
854 | 854 | } |
|
855 | 855 | |
|
856 | 856 | button + .help-block { |
|
857 | 857 | margin-top: @padding; |
|
858 | 858 | } |
|
859 | 859 | } |
|
860 | 860 | |
|
861 | 861 | // admin settings radio buttons and labels |
|
862 | 862 | .label-2 { |
|
863 | 863 | float: left; |
|
864 | 864 | width: @label2-width; |
|
865 | 865 | |
|
866 | 866 | label { |
|
867 | 867 | color: @grey1; |
|
868 | 868 | } |
|
869 | 869 | } |
|
870 | 870 | .checkboxes { |
|
871 | 871 | float: left; |
|
872 | 872 | width: @checkboxes-width; |
|
873 | 873 | margin-bottom: @padding; |
|
874 | 874 | |
|
875 | 875 | .checkbox { |
|
876 | 876 | width: 100%; |
|
877 | 877 | |
|
878 | 878 | label { |
|
879 | 879 | margin: 0; |
|
880 | 880 | padding: 0; |
|
881 | 881 | } |
|
882 | 882 | } |
|
883 | 883 | |
|
884 | 884 | .checkbox + .checkbox { |
|
885 | 885 | display: inline-block; |
|
886 | 886 | } |
|
887 | 887 | |
|
888 | 888 | label { |
|
889 | 889 | margin-right: 1em; |
|
890 | 890 | } |
|
891 | 891 | } |
|
892 | 892 | |
|
893 | 893 | // CHANGELOG |
|
894 | 894 | .container_header { |
|
895 | 895 | float: left; |
|
896 | 896 | display: block; |
|
897 | 897 | width: 100%; |
|
898 | 898 | margin: @padding 0 @padding; |
|
899 | 899 | |
|
900 | 900 | #filter_changelog { |
|
901 | 901 | float: left; |
|
902 | 902 | margin-right: @padding; |
|
903 | 903 | } |
|
904 | 904 | |
|
905 | 905 | .breadcrumbs_light { |
|
906 | 906 | display: inline-block; |
|
907 | 907 | } |
|
908 | 908 | } |
|
909 | 909 | |
|
910 | 910 | .info_box { |
|
911 | 911 | float: right; |
|
912 | 912 | } |
|
913 | 913 | |
|
914 | 914 | |
|
915 | 915 | #graph_nodes { |
|
916 | 916 | padding-top: 43px; |
|
917 | 917 | } |
|
918 | 918 | |
|
919 | 919 | #graph_content{ |
|
920 | 920 | |
|
921 | 921 | // adjust for table headers so that graph renders properly |
|
922 | 922 | // #graph_nodes padding - table cell padding |
|
923 | 923 | padding-top: (@space - (@basefontsize * 2.4)); |
|
924 | 924 | |
|
925 | 925 | &.graph_full_width { |
|
926 | 926 | width: 100%; |
|
927 | 927 | max-width: 100%; |
|
928 | 928 | } |
|
929 | 929 | } |
|
930 | 930 | |
|
931 | 931 | #graph { |
|
932 | 932 | .flag_status { |
|
933 | 933 | margin: 0; |
|
934 | 934 | } |
|
935 | 935 | |
|
936 | 936 | .pagination-left { |
|
937 | 937 | float: left; |
|
938 | 938 | clear: both; |
|
939 | 939 | } |
|
940 | 940 | |
|
941 | 941 | .log-container { |
|
942 | 942 | max-width: 345px; |
|
943 | 943 | |
|
944 | 944 | .message{ |
|
945 | 945 | max-width: 340px; |
|
946 | 946 | } |
|
947 | 947 | } |
|
948 | 948 | |
|
949 | 949 | .graph-col-wrapper { |
|
950 | 950 | padding-left: 110px; |
|
951 | 951 | |
|
952 | 952 | #graph_nodes { |
|
953 | 953 | width: 100px; |
|
954 | 954 | margin-left: -110px; |
|
955 | 955 | float: left; |
|
956 | 956 | clear: left; |
|
957 | 957 | } |
|
958 | 958 | } |
|
959 | 959 | } |
|
960 | 960 | |
|
961 | 961 | #filter_changelog { |
|
962 | 962 | float: left; |
|
963 | 963 | } |
|
964 | 964 | |
|
965 | 965 | |
|
966 | 966 | //--- THEME ------------------// |
|
967 | 967 | |
|
968 | 968 | #logo { |
|
969 | 969 | float: left; |
|
970 | 970 | margin: 9px 0 0 0; |
|
971 | 971 | |
|
972 | 972 | .header { |
|
973 | 973 | background-color: transparent; |
|
974 | 974 | } |
|
975 | 975 | |
|
976 | 976 | a { |
|
977 | 977 | display: inline-block; |
|
978 | 978 | } |
|
979 | 979 | |
|
980 | 980 | img { |
|
981 | 981 | height:30px; |
|
982 | 982 | } |
|
983 | 983 | } |
|
984 | 984 | |
|
985 | 985 | .logo-wrapper { |
|
986 | 986 | float:left; |
|
987 | 987 | } |
|
988 | 988 | |
|
989 | 989 | .branding{ |
|
990 | 990 | float: left; |
|
991 | 991 | padding: 9px 2px; |
|
992 | 992 | line-height: 1em; |
|
993 | 993 | font-size: @navigation-fontsize; |
|
994 | 994 | } |
|
995 | 995 | |
|
996 | 996 | img { |
|
997 | 997 | border: none; |
|
998 | 998 | outline: none; |
|
999 | 999 | } |
|
1000 | 1000 | user-profile-header |
|
1001 | 1001 | label { |
|
1002 | 1002 | |
|
1003 | 1003 | input[type="checkbox"] { |
|
1004 | 1004 | margin-right: 1em; |
|
1005 | 1005 | } |
|
1006 | 1006 | input[type="radio"] { |
|
1007 | 1007 | margin-right: 1em; |
|
1008 | 1008 | } |
|
1009 | 1009 | } |
|
1010 | 1010 | |
|
1011 | 1011 | .flag_status { |
|
1012 | 1012 | margin: 2px 8px 6px 2px; |
|
1013 | 1013 | &.under_review { |
|
1014 | 1014 | .circle(5px, @alert3); |
|
1015 | 1015 | } |
|
1016 | 1016 | &.approved { |
|
1017 | 1017 | .circle(5px, @alert1); |
|
1018 | 1018 | } |
|
1019 | 1019 | &.rejected, |
|
1020 | 1020 | &.forced_closed{ |
|
1021 | 1021 | .circle(5px, @alert2); |
|
1022 | 1022 | } |
|
1023 | 1023 | &.not_reviewed { |
|
1024 | 1024 | .circle(5px, @grey5); |
|
1025 | 1025 | } |
|
1026 | 1026 | } |
|
1027 | 1027 | |
|
1028 | 1028 | .flag_status_comment_box { |
|
1029 | 1029 | margin: 5px 6px 0px 2px; |
|
1030 | 1030 | } |
|
1031 | 1031 | .test_pattern_preview { |
|
1032 | 1032 | margin: @space 0; |
|
1033 | 1033 | |
|
1034 | 1034 | p { |
|
1035 | 1035 | margin-bottom: 0; |
|
1036 | 1036 | border-bottom: @border-thickness solid @border-default-color; |
|
1037 | 1037 | color: @grey3; |
|
1038 | 1038 | } |
|
1039 | 1039 | |
|
1040 | 1040 | .btn { |
|
1041 | 1041 | margin-bottom: @padding; |
|
1042 | 1042 | } |
|
1043 | 1043 | } |
|
1044 | 1044 | #test_pattern_result { |
|
1045 | 1045 | display: none; |
|
1046 | 1046 | &:extend(pre); |
|
1047 | 1047 | padding: .9em; |
|
1048 | 1048 | color: @grey3; |
|
1049 | 1049 | background-color: @grey7; |
|
1050 | 1050 | border-right: @border-thickness solid @border-default-color; |
|
1051 | 1051 | border-bottom: @border-thickness solid @border-default-color; |
|
1052 | 1052 | border-left: @border-thickness solid @border-default-color; |
|
1053 | 1053 | } |
|
1054 | 1054 | |
|
1055 | 1055 | #repo_vcs_settings { |
|
1056 | 1056 | #inherit_overlay_vcs_default { |
|
1057 | 1057 | display: none; |
|
1058 | 1058 | } |
|
1059 | 1059 | #inherit_overlay_vcs_custom { |
|
1060 | 1060 | display: custom; |
|
1061 | 1061 | } |
|
1062 | 1062 | &.inherited { |
|
1063 | 1063 | #inherit_overlay_vcs_default { |
|
1064 | 1064 | display: block; |
|
1065 | 1065 | } |
|
1066 | 1066 | #inherit_overlay_vcs_custom { |
|
1067 | 1067 | display: none; |
|
1068 | 1068 | } |
|
1069 | 1069 | } |
|
1070 | 1070 | } |
|
1071 | 1071 | |
|
1072 | 1072 | .issue-tracker-link { |
|
1073 | 1073 | color: @rcblue; |
|
1074 | 1074 | } |
|
1075 | 1075 | |
|
1076 | 1076 | // Issue Tracker Table Show/Hide |
|
1077 | 1077 | #repo_issue_tracker { |
|
1078 | 1078 | #inherit_overlay { |
|
1079 | 1079 | display: none; |
|
1080 | 1080 | } |
|
1081 | 1081 | #custom_overlay { |
|
1082 | 1082 | display: custom; |
|
1083 | 1083 | } |
|
1084 | 1084 | &.inherited { |
|
1085 | 1085 | #inherit_overlay { |
|
1086 | 1086 | display: block; |
|
1087 | 1087 | } |
|
1088 | 1088 | #custom_overlay { |
|
1089 | 1089 | display: none; |
|
1090 | 1090 | } |
|
1091 | 1091 | } |
|
1092 | 1092 | } |
|
1093 | 1093 | table.issuetracker { |
|
1094 | 1094 | &.readonly { |
|
1095 | 1095 | tr, td { |
|
1096 | 1096 | color: @grey3; |
|
1097 | 1097 | } |
|
1098 | 1098 | } |
|
1099 | 1099 | .edit { |
|
1100 | 1100 | display: none; |
|
1101 | 1101 | } |
|
1102 | 1102 | .editopen { |
|
1103 | 1103 | .edit { |
|
1104 | 1104 | display: inline; |
|
1105 | 1105 | } |
|
1106 | 1106 | .entry { |
|
1107 | 1107 | display: none; |
|
1108 | 1108 | } |
|
1109 | 1109 | } |
|
1110 | 1110 | tr td.td-action { |
|
1111 | 1111 | min-width: 117px; |
|
1112 | 1112 | } |
|
1113 | 1113 | td input { |
|
1114 | 1114 | max-width: none; |
|
1115 | 1115 | min-width: 30px; |
|
1116 | 1116 | width: 80%; |
|
1117 | 1117 | } |
|
1118 | 1118 | .issuetracker_pref input { |
|
1119 | 1119 | width: 40%; |
|
1120 | 1120 | } |
|
1121 | 1121 | input.edit_issuetracker_update { |
|
1122 | 1122 | margin-right: 0; |
|
1123 | 1123 | width: auto; |
|
1124 | 1124 | } |
|
1125 | 1125 | } |
|
1126 | 1126 | |
|
1127 | 1127 | table.integrations { |
|
1128 | 1128 | .td-icon { |
|
1129 | 1129 | width: 20px; |
|
1130 | 1130 | .integration-icon { |
|
1131 | 1131 | height: 20px; |
|
1132 | 1132 | width: 20px; |
|
1133 | 1133 | } |
|
1134 | 1134 | } |
|
1135 | 1135 | } |
|
1136 | 1136 | |
|
1137 | 1137 | .integrations { |
|
1138 | 1138 | a.integration-box { |
|
1139 | 1139 | color: @text-color; |
|
1140 | 1140 | &:hover { |
|
1141 | 1141 | .panel { |
|
1142 | 1142 | background: #fbfbfb; |
|
1143 | 1143 | } |
|
1144 | 1144 | } |
|
1145 | 1145 | .integration-icon { |
|
1146 | 1146 | width: 30px; |
|
1147 | 1147 | height: 30px; |
|
1148 | 1148 | margin-right: 20px; |
|
1149 | 1149 | float: left; |
|
1150 | 1150 | } |
|
1151 | 1151 | |
|
1152 | 1152 | .panel-body { |
|
1153 | 1153 | padding: 10px; |
|
1154 | 1154 | } |
|
1155 | 1155 | .panel { |
|
1156 | 1156 | margin-bottom: 10px; |
|
1157 | 1157 | } |
|
1158 | 1158 | h2 { |
|
1159 | 1159 | display: inline-block; |
|
1160 | 1160 | margin: 0; |
|
1161 | 1161 | min-width: 140px; |
|
1162 | 1162 | } |
|
1163 | 1163 | } |
|
1164 | 1164 | } |
|
1165 | 1165 | |
|
1166 | 1166 | //Permissions Settings |
|
1167 | 1167 | #add_perm { |
|
1168 | 1168 | margin: 0 0 @padding; |
|
1169 | 1169 | cursor: pointer; |
|
1170 | 1170 | } |
|
1171 | 1171 | |
|
1172 | 1172 | .perm_ac { |
|
1173 | 1173 | input { |
|
1174 | 1174 | width: 95%; |
|
1175 | 1175 | } |
|
1176 | 1176 | } |
|
1177 | 1177 | |
|
1178 | 1178 | .autocomplete-suggestions { |
|
1179 | 1179 | width: auto !important; // overrides autocomplete.js |
|
1180 | 1180 | margin: 0; |
|
1181 | 1181 | border: @border-thickness solid @rcblue; |
|
1182 | 1182 | border-radius: @border-radius; |
|
1183 | 1183 | color: @rcblue; |
|
1184 | 1184 | background-color: white; |
|
1185 | 1185 | } |
|
1186 | 1186 | .autocomplete-selected { |
|
1187 | 1187 | background: #F0F0F0; |
|
1188 | 1188 | } |
|
1189 | 1189 | .ac-container-wrap { |
|
1190 | 1190 | margin: 0; |
|
1191 | 1191 | padding: 8px; |
|
1192 | 1192 | border-bottom: @border-thickness solid @rclightblue; |
|
1193 | 1193 | list-style-type: none; |
|
1194 | 1194 | cursor: pointer; |
|
1195 | 1195 | |
|
1196 | 1196 | &:hover { |
|
1197 | 1197 | background-color: @rclightblue; |
|
1198 | 1198 | } |
|
1199 | 1199 | |
|
1200 | 1200 | img { |
|
1201 | 1201 | height: @gravatar-size; |
|
1202 | 1202 | width: @gravatar-size; |
|
1203 | 1203 | margin-right: 1em; |
|
1204 | 1204 | } |
|
1205 | 1205 | |
|
1206 | 1206 | strong { |
|
1207 | 1207 | font-weight: normal; |
|
1208 | 1208 | } |
|
1209 | 1209 | } |
|
1210 | 1210 | |
|
1211 | 1211 | // Settings Dropdown |
|
1212 | 1212 | .user-menu .container { |
|
1213 | 1213 | padding: 0 4px; |
|
1214 | 1214 | margin: 0; |
|
1215 | 1215 | } |
|
1216 | 1216 | |
|
1217 | 1217 | .user-menu .gravatar { |
|
1218 | 1218 | cursor: pointer; |
|
1219 | 1219 | } |
|
1220 | 1220 | |
|
1221 | 1221 | .codeblock { |
|
1222 | 1222 | margin-bottom: @padding; |
|
1223 | 1223 | clear: both; |
|
1224 | 1224 | |
|
1225 | 1225 | .stats{ |
|
1226 | 1226 | overflow: hidden; |
|
1227 | 1227 | } |
|
1228 | 1228 | |
|
1229 | 1229 | .message{ |
|
1230 | 1230 | textarea{ |
|
1231 | 1231 | margin: 0; |
|
1232 | 1232 | } |
|
1233 | 1233 | } |
|
1234 | 1234 | |
|
1235 | 1235 | .code-header { |
|
1236 | 1236 | .stats { |
|
1237 | 1237 | line-height: 2em; |
|
1238 | 1238 | |
|
1239 | 1239 | .revision_id { |
|
1240 | 1240 | margin-left: 0; |
|
1241 | 1241 | } |
|
1242 | 1242 | .buttons { |
|
1243 | 1243 | padding-right: 0; |
|
1244 | 1244 | } |
|
1245 | 1245 | } |
|
1246 | 1246 | |
|
1247 | 1247 | .item{ |
|
1248 | 1248 | margin-right: 0.5em; |
|
1249 | 1249 | } |
|
1250 | 1250 | } |
|
1251 | 1251 | |
|
1252 | 1252 | #editor_container{ |
|
1253 | 1253 | position: relative; |
|
1254 | 1254 | margin: @padding; |
|
1255 | 1255 | } |
|
1256 | 1256 | } |
|
1257 | 1257 | |
|
1258 | 1258 | #file_history_container { |
|
1259 | 1259 | display: none; |
|
1260 | 1260 | } |
|
1261 | 1261 | |
|
1262 | 1262 | .file-history-inner { |
|
1263 | 1263 | margin-bottom: 10px; |
|
1264 | 1264 | } |
|
1265 | 1265 | |
|
1266 | 1266 | // Pull Requests |
|
1267 | 1267 | .summary-details { |
|
1268 | 1268 | width: 72%; |
|
1269 | 1269 | } |
|
1270 | 1270 | .pr-summary { |
|
1271 | 1271 | border-bottom: @border-thickness solid @grey5; |
|
1272 | 1272 | margin-bottom: @space; |
|
1273 | 1273 | } |
|
1274 | 1274 | .reviewers-title { |
|
1275 | 1275 | width: 25%; |
|
1276 | 1276 | min-width: 200px; |
|
1277 | 1277 | } |
|
1278 | 1278 | .reviewers { |
|
1279 | 1279 | width: 25%; |
|
1280 | 1280 | min-width: 200px; |
|
1281 | 1281 | } |
|
1282 | 1282 | .reviewers ul li { |
|
1283 | 1283 | position: relative; |
|
1284 | 1284 | width: 100%; |
|
1285 | 1285 | margin-bottom: 8px; |
|
1286 | 1286 | } |
|
1287 | 1287 | .reviewers_member { |
|
1288 | 1288 | width: 100%; |
|
1289 | 1289 | overflow: auto; |
|
1290 | 1290 | } |
|
1291 | 1291 | .reviewer_reason { |
|
1292 | 1292 | padding-left: 20px; |
|
1293 | 1293 | } |
|
1294 | 1294 | .reviewer_status { |
|
1295 | 1295 | display: inline-block; |
|
1296 | 1296 | vertical-align: top; |
|
1297 | 1297 | width: 7%; |
|
1298 | 1298 | min-width: 20px; |
|
1299 | 1299 | height: 1.2em; |
|
1300 | 1300 | margin-top: 3px; |
|
1301 | 1301 | line-height: 1em; |
|
1302 | 1302 | } |
|
1303 | 1303 | |
|
1304 | 1304 | .reviewer_name { |
|
1305 | 1305 | display: inline-block; |
|
1306 | 1306 | max-width: 83%; |
|
1307 | 1307 | padding-right: 20px; |
|
1308 | 1308 | vertical-align: middle; |
|
1309 | 1309 | line-height: 1; |
|
1310 | 1310 | |
|
1311 | 1311 | .rc-user { |
|
1312 | 1312 | min-width: 0; |
|
1313 | 1313 | margin: -2px 1em 0 0; |
|
1314 | 1314 | } |
|
1315 | 1315 | |
|
1316 | 1316 | .reviewer { |
|
1317 | 1317 | float: left; |
|
1318 | 1318 | } |
|
1319 | 1319 | |
|
1320 | 1320 | &.to-delete { |
|
1321 | 1321 | .user, |
|
1322 | 1322 | .reviewer { |
|
1323 | 1323 | text-decoration: line-through; |
|
1324 | 1324 | } |
|
1325 | 1325 | } |
|
1326 | 1326 | } |
|
1327 | 1327 | |
|
1328 | 1328 | .reviewer_member_remove { |
|
1329 | 1329 | position: absolute; |
|
1330 | 1330 | right: 0; |
|
1331 | 1331 | top: 0; |
|
1332 | 1332 | width: 16px; |
|
1333 | 1333 | margin-bottom: 10px; |
|
1334 | 1334 | padding: 0; |
|
1335 | 1335 | color: black; |
|
1336 | 1336 | } |
|
1337 | 1337 | .reviewer_member_status { |
|
1338 | 1338 | margin-top: 5px; |
|
1339 | 1339 | } |
|
1340 | 1340 | .pr-summary #summary{ |
|
1341 | 1341 | width: 100%; |
|
1342 | 1342 | } |
|
1343 | 1343 | .pr-summary .action_button:hover { |
|
1344 | 1344 | border: 0; |
|
1345 | 1345 | cursor: pointer; |
|
1346 | 1346 | } |
|
1347 | 1347 | .pr-details-title { |
|
1348 | 1348 | padding-bottom: 8px; |
|
1349 | 1349 | border-bottom: @border-thickness solid @grey5; |
|
1350 | 1350 | |
|
1351 | 1351 | .action_button.disabled { |
|
1352 | 1352 | color: @grey4; |
|
1353 | 1353 | cursor: inherit; |
|
1354 | 1354 | } |
|
1355 | 1355 | .action_button { |
|
1356 | 1356 | color: @rcblue; |
|
1357 | 1357 | } |
|
1358 | 1358 | } |
|
1359 | 1359 | .pr-details-content { |
|
1360 | 1360 | margin-top: @textmargin; |
|
1361 | 1361 | margin-bottom: @textmargin; |
|
1362 | 1362 | } |
|
1363 | 1363 | .pr-description { |
|
1364 | 1364 | white-space:pre-wrap; |
|
1365 | 1365 | } |
|
1366 | 1366 | .group_members { |
|
1367 | 1367 | margin-top: 0; |
|
1368 | 1368 | padding: 0; |
|
1369 | 1369 | list-style: outside none none; |
|
1370 | 1370 | |
|
1371 | 1371 | img { |
|
1372 | 1372 | height: @gravatar-size; |
|
1373 | 1373 | width: @gravatar-size; |
|
1374 | 1374 | margin-right: .5em; |
|
1375 | 1375 | margin-left: 3px; |
|
1376 | 1376 | } |
|
1377 | 1377 | |
|
1378 | 1378 | .to-delete { |
|
1379 | 1379 | .user { |
|
1380 | 1380 | text-decoration: line-through; |
|
1381 | 1381 | } |
|
1382 | 1382 | } |
|
1383 | 1383 | } |
|
1384 | 1384 | |
|
1385 | 1385 | .compare_view_commits_title { |
|
1386 | 1386 | .disabled { |
|
1387 | 1387 | cursor: inherit; |
|
1388 | 1388 | &:hover{ |
|
1389 | 1389 | background-color: inherit; |
|
1390 | 1390 | color: inherit; |
|
1391 | 1391 | } |
|
1392 | 1392 | } |
|
1393 | 1393 | } |
|
1394 | 1394 | |
|
1395 | 1395 | // new entry in group_members |
|
1396 | 1396 | .td-author-new-entry { |
|
1397 | 1397 | background-color: rgba(red(@alert1), green(@alert1), blue(@alert1), 0.3); |
|
1398 | 1398 | } |
|
1399 | 1399 | |
|
1400 | 1400 | .usergroup_member_remove { |
|
1401 | 1401 | width: 16px; |
|
1402 | 1402 | margin-bottom: 10px; |
|
1403 | 1403 | padding: 0; |
|
1404 | 1404 | color: black !important; |
|
1405 | 1405 | cursor: pointer; |
|
1406 | 1406 | } |
|
1407 | 1407 | |
|
1408 | 1408 | .reviewer_ac .ac-input { |
|
1409 | 1409 | width: 92%; |
|
1410 | 1410 | margin-bottom: 1em; |
|
1411 | 1411 | } |
|
1412 | 1412 | |
|
1413 | 1413 | .compare_view_commits tr{ |
|
1414 | 1414 | height: 20px; |
|
1415 | 1415 | } |
|
1416 | 1416 | .compare_view_commits td { |
|
1417 | 1417 | vertical-align: top; |
|
1418 | 1418 | padding-top: 10px; |
|
1419 | 1419 | } |
|
1420 | 1420 | .compare_view_commits .author { |
|
1421 | 1421 | margin-left: 5px; |
|
1422 | 1422 | } |
|
1423 | 1423 | |
|
1424 | 1424 | .compare_view_files { |
|
1425 | 1425 | width: 100%; |
|
1426 | 1426 | |
|
1427 | 1427 | td { |
|
1428 | 1428 | vertical-align: middle; |
|
1429 | 1429 | } |
|
1430 | 1430 | } |
|
1431 | 1431 | |
|
1432 | 1432 | .compare_view_filepath { |
|
1433 | 1433 | color: @grey1; |
|
1434 | 1434 | } |
|
1435 | 1435 | |
|
1436 | 1436 | .show_more { |
|
1437 | 1437 | display: inline-block; |
|
1438 | 1438 | position: relative; |
|
1439 | 1439 | vertical-align: middle; |
|
1440 | 1440 | width: 4px; |
|
1441 | 1441 | height: @basefontsize; |
|
1442 | 1442 | |
|
1443 | 1443 | &:after { |
|
1444 | 1444 | content: "\00A0\25BE"; |
|
1445 | 1445 | display: inline-block; |
|
1446 | 1446 | width:10px; |
|
1447 | 1447 | line-height: 5px; |
|
1448 | 1448 | font-size: 12px; |
|
1449 | 1449 | cursor: pointer; |
|
1450 | 1450 | } |
|
1451 | 1451 | } |
|
1452 | 1452 | |
|
1453 | 1453 | .journal_more .show_more { |
|
1454 | 1454 | display: inline; |
|
1455 | 1455 | |
|
1456 | 1456 | &:after { |
|
1457 | 1457 | content: none; |
|
1458 | 1458 | } |
|
1459 | 1459 | } |
|
1460 | 1460 | |
|
1461 | 1461 | .open .show_more:after, |
|
1462 | 1462 | .select2-dropdown-open .show_more:after { |
|
1463 | 1463 | .rotate(180deg); |
|
1464 | 1464 | margin-left: 4px; |
|
1465 | 1465 | } |
|
1466 | 1466 | |
|
1467 | 1467 | |
|
1468 | 1468 | .compare_view_commits .collapse_commit:after { |
|
1469 | 1469 | cursor: pointer; |
|
1470 | 1470 | content: "\00A0\25B4"; |
|
1471 | 1471 | margin-left: -3px; |
|
1472 | 1472 | font-size: 17px; |
|
1473 | 1473 | color: @grey4; |
|
1474 | 1474 | } |
|
1475 | 1475 | |
|
1476 | 1476 | .diff_links { |
|
1477 | 1477 | margin-left: 8px; |
|
1478 | 1478 | } |
|
1479 | 1479 | |
|
1480 | 1480 | div.ancestor { |
|
1481 | 1481 | margin: @padding 0; |
|
1482 | 1482 | line-height: 3.0em; |
|
1483 | 1483 | } |
|
1484 | 1484 | |
|
1485 | 1485 | .cs_icon_td input[type="checkbox"] { |
|
1486 | 1486 | display: none; |
|
1487 | 1487 | } |
|
1488 | 1488 | |
|
1489 | 1489 | .cs_icon_td .expand_file_icon:after { |
|
1490 | 1490 | cursor: pointer; |
|
1491 | 1491 | content: "\00A0\25B6"; |
|
1492 | 1492 | font-size: 12px; |
|
1493 | 1493 | color: @grey4; |
|
1494 | 1494 | } |
|
1495 | 1495 | |
|
1496 | 1496 | .cs_icon_td .collapse_file_icon:after { |
|
1497 | 1497 | cursor: pointer; |
|
1498 | 1498 | content: "\00A0\25BC"; |
|
1499 | 1499 | font-size: 12px; |
|
1500 | 1500 | color: @grey4; |
|
1501 | 1501 | } |
|
1502 | 1502 | |
|
1503 | 1503 | /*new binary |
|
1504 | 1504 | NEW_FILENODE = 1 |
|
1505 | 1505 | DEL_FILENODE = 2 |
|
1506 | 1506 | MOD_FILENODE = 3 |
|
1507 | 1507 | RENAMED_FILENODE = 4 |
|
1508 | 1508 | COPIED_FILENODE = 5 |
|
1509 | 1509 | CHMOD_FILENODE = 6 |
|
1510 | 1510 | BIN_FILENODE = 7 |
|
1511 | 1511 | */ |
|
1512 | 1512 | .cs_files_expand { |
|
1513 | 1513 | font-size: @basefontsize + 5px; |
|
1514 | 1514 | line-height: 1.8em; |
|
1515 | 1515 | float: right; |
|
1516 | 1516 | } |
|
1517 | 1517 | |
|
1518 | 1518 | .cs_files_expand span{ |
|
1519 | 1519 | color: @rcblue; |
|
1520 | 1520 | cursor: pointer; |
|
1521 | 1521 | } |
|
1522 | 1522 | .cs_files { |
|
1523 | 1523 | clear: both; |
|
1524 | 1524 | padding-bottom: @padding; |
|
1525 | 1525 | |
|
1526 | 1526 | .cur_cs { |
|
1527 | 1527 | margin: 10px 2px; |
|
1528 | 1528 | font-weight: bold; |
|
1529 | 1529 | } |
|
1530 | 1530 | |
|
1531 | 1531 | .node { |
|
1532 | 1532 | float: left; |
|
1533 | 1533 | } |
|
1534 | 1534 | |
|
1535 | 1535 | .changes { |
|
1536 | 1536 | float: right; |
|
1537 | 1537 | color: white; |
|
1538 | 1538 | font-size: @basefontsize - 4px; |
|
1539 | 1539 | margin-top: 4px; |
|
1540 | 1540 | opacity: 0.6; |
|
1541 | 1541 | filter: Alpha(opacity=60); /* IE8 and earlier */ |
|
1542 | 1542 | |
|
1543 | 1543 | .added { |
|
1544 | 1544 | background-color: @alert1; |
|
1545 | 1545 | float: left; |
|
1546 | 1546 | text-align: center; |
|
1547 | 1547 | } |
|
1548 | 1548 | |
|
1549 | 1549 | .deleted { |
|
1550 | 1550 | background-color: @alert2; |
|
1551 | 1551 | float: left; |
|
1552 | 1552 | text-align: center; |
|
1553 | 1553 | } |
|
1554 | 1554 | |
|
1555 | 1555 | .bin { |
|
1556 | 1556 | background-color: @alert1; |
|
1557 | 1557 | text-align: center; |
|
1558 | 1558 | } |
|
1559 | 1559 | |
|
1560 | 1560 | /*new binary*/ |
|
1561 | 1561 | .bin.bin1 { |
|
1562 | 1562 | background-color: @alert1; |
|
1563 | 1563 | text-align: center; |
|
1564 | 1564 | } |
|
1565 | 1565 | |
|
1566 | 1566 | /*deleted binary*/ |
|
1567 | 1567 | .bin.bin2 { |
|
1568 | 1568 | background-color: @alert2; |
|
1569 | 1569 | text-align: center; |
|
1570 | 1570 | } |
|
1571 | 1571 | |
|
1572 | 1572 | /*mod binary*/ |
|
1573 | 1573 | .bin.bin3 { |
|
1574 | 1574 | background-color: @grey2; |
|
1575 | 1575 | text-align: center; |
|
1576 | 1576 | } |
|
1577 | 1577 | |
|
1578 | 1578 | /*rename file*/ |
|
1579 | 1579 | .bin.bin4 { |
|
1580 | 1580 | background-color: @alert4; |
|
1581 | 1581 | text-align: center; |
|
1582 | 1582 | } |
|
1583 | 1583 | |
|
1584 | 1584 | /*copied file*/ |
|
1585 | 1585 | .bin.bin5 { |
|
1586 | 1586 | background-color: @alert4; |
|
1587 | 1587 | text-align: center; |
|
1588 | 1588 | } |
|
1589 | 1589 | |
|
1590 | 1590 | /*chmod file*/ |
|
1591 | 1591 | .bin.bin6 { |
|
1592 | 1592 | background-color: @grey2; |
|
1593 | 1593 | text-align: center; |
|
1594 | 1594 | } |
|
1595 | 1595 | } |
|
1596 | 1596 | } |
|
1597 | 1597 | |
|
1598 | 1598 | .cs_files .cs_added, .cs_files .cs_A, |
|
1599 | 1599 | .cs_files .cs_added, .cs_files .cs_M, |
|
1600 | 1600 | .cs_files .cs_added, .cs_files .cs_D { |
|
1601 | 1601 | height: 16px; |
|
1602 | 1602 | padding-right: 10px; |
|
1603 | 1603 | margin-top: 7px; |
|
1604 | 1604 | text-align: left; |
|
1605 | 1605 | } |
|
1606 | 1606 | |
|
1607 | 1607 | .cs_icon_td { |
|
1608 | 1608 | min-width: 16px; |
|
1609 | 1609 | width: 16px; |
|
1610 | 1610 | } |
|
1611 | 1611 | |
|
1612 | 1612 | .pull-request-merge { |
|
1613 | 1613 | padding: 10px 0; |
|
1614 | 1614 | margin-top: 10px; |
|
1615 | 1615 | margin-bottom: 20px; |
|
1616 | 1616 | } |
|
1617 | 1617 | |
|
1618 | 1618 | .pull-request-merge .pull-request-wrap { |
|
1619 | 1619 | height: 25px; |
|
1620 | 1620 | padding: 5px 0; |
|
1621 | 1621 | } |
|
1622 | 1622 | |
|
1623 | 1623 | .pull-request-merge span { |
|
1624 | 1624 | margin-right: 10px; |
|
1625 | 1625 | } |
|
1626 | ||
|
1627 | .pr-versions { | |
|
1628 | position: relative; | |
|
1629 | top: 6px; | |
|
1630 | } | |
|
1631 | ||
|
1626 | 1632 | #close_pull_request { |
|
1627 | 1633 | margin-right: 0px; |
|
1628 | 1634 | } |
|
1629 | 1635 | |
|
1630 | 1636 | .empty_data { |
|
1631 | 1637 | color: @grey4; |
|
1632 | 1638 | } |
|
1633 | 1639 | |
|
1634 | 1640 | #changeset_compare_view_content { |
|
1635 | 1641 | margin-bottom: @space; |
|
1636 | 1642 | clear: both; |
|
1637 | 1643 | width: 100%; |
|
1638 | 1644 | box-sizing: border-box; |
|
1639 | 1645 | .border-radius(@border-radius); |
|
1640 | 1646 | |
|
1641 | 1647 | .help-block { |
|
1642 | 1648 | margin: @padding 0; |
|
1643 | 1649 | color: @text-color; |
|
1644 | 1650 | } |
|
1645 | 1651 | |
|
1646 | 1652 | .empty_data { |
|
1647 | 1653 | margin: @padding 0; |
|
1648 | 1654 | } |
|
1649 | 1655 | |
|
1650 | 1656 | .alert { |
|
1651 | 1657 | margin-bottom: @space; |
|
1652 | 1658 | } |
|
1653 | 1659 | } |
|
1654 | 1660 | |
|
1655 | 1661 | .table_disp { |
|
1656 | 1662 | .status { |
|
1657 | 1663 | width: auto; |
|
1658 | 1664 | |
|
1659 | 1665 | .flag_status { |
|
1660 | 1666 | float: left; |
|
1661 | 1667 | } |
|
1662 | 1668 | } |
|
1663 | 1669 | } |
|
1664 | 1670 | |
|
1665 | 1671 | .status_box_menu { |
|
1666 | 1672 | margin: 0; |
|
1667 | 1673 | } |
|
1668 | 1674 | |
|
1669 | 1675 | .notification-table{ |
|
1670 | 1676 | margin-bottom: @space; |
|
1671 | 1677 | display: table; |
|
1672 | 1678 | width: 100%; |
|
1673 | 1679 | |
|
1674 | 1680 | .container{ |
|
1675 | 1681 | display: table-row; |
|
1676 | 1682 | |
|
1677 | 1683 | .notification-header{ |
|
1678 | 1684 | border-bottom: @border-thickness solid @border-default-color; |
|
1679 | 1685 | } |
|
1680 | 1686 | |
|
1681 | 1687 | .notification-subject{ |
|
1682 | 1688 | display: table-cell; |
|
1683 | 1689 | } |
|
1684 | 1690 | } |
|
1685 | 1691 | } |
|
1686 | 1692 | |
|
1687 | 1693 | // Notifications |
|
1688 | 1694 | .notification-header{ |
|
1689 | 1695 | display: table; |
|
1690 | 1696 | width: 100%; |
|
1691 | 1697 | padding: floor(@basefontsize/2) 0; |
|
1692 | 1698 | line-height: 1em; |
|
1693 | 1699 | |
|
1694 | 1700 | .desc, .delete-notifications, .read-notifications{ |
|
1695 | 1701 | display: table-cell; |
|
1696 | 1702 | text-align: left; |
|
1697 | 1703 | } |
|
1698 | 1704 | |
|
1699 | 1705 | .desc{ |
|
1700 | 1706 | width: 1163px; |
|
1701 | 1707 | } |
|
1702 | 1708 | |
|
1703 | 1709 | .delete-notifications, .read-notifications{ |
|
1704 | 1710 | width: 35px; |
|
1705 | 1711 | min-width: 35px; //fixes when only one button is displayed |
|
1706 | 1712 | } |
|
1707 | 1713 | } |
|
1708 | 1714 | |
|
1709 | 1715 | .notification-body { |
|
1710 | 1716 | .markdown-block, |
|
1711 | 1717 | .rst-block { |
|
1712 | 1718 | padding: @padding 0; |
|
1713 | 1719 | } |
|
1714 | 1720 | |
|
1715 | 1721 | .notification-subject { |
|
1716 | 1722 | padding: @textmargin 0; |
|
1717 | 1723 | border-bottom: @border-thickness solid @border-default-color; |
|
1718 | 1724 | } |
|
1719 | 1725 | } |
|
1720 | 1726 | |
|
1721 | 1727 | |
|
1722 | 1728 | .notifications_buttons{ |
|
1723 | 1729 | float: right; |
|
1724 | 1730 | } |
|
1725 | 1731 | |
|
1726 | 1732 | #notification-status{ |
|
1727 | 1733 | display: inline; |
|
1728 | 1734 | } |
|
1729 | 1735 | |
|
1730 | 1736 | // Repositories |
|
1731 | 1737 | |
|
1732 | 1738 | #summary.fields{ |
|
1733 | 1739 | display: table; |
|
1734 | 1740 | |
|
1735 | 1741 | .field{ |
|
1736 | 1742 | display: table-row; |
|
1737 | 1743 | |
|
1738 | 1744 | .label-summary{ |
|
1739 | 1745 | display: table-cell; |
|
1740 | 1746 | min-width: @label-summary-minwidth; |
|
1741 | 1747 | padding-top: @padding/2; |
|
1742 | 1748 | padding-bottom: @padding/2; |
|
1743 | 1749 | padding-right: @padding/2; |
|
1744 | 1750 | } |
|
1745 | 1751 | |
|
1746 | 1752 | .input{ |
|
1747 | 1753 | display: table-cell; |
|
1748 | 1754 | padding: @padding/2; |
|
1749 | 1755 | |
|
1750 | 1756 | input{ |
|
1751 | 1757 | min-width: 29em; |
|
1752 | 1758 | padding: @padding/4; |
|
1753 | 1759 | } |
|
1754 | 1760 | } |
|
1755 | 1761 | .statistics, .downloads{ |
|
1756 | 1762 | .disabled{ |
|
1757 | 1763 | color: @grey4; |
|
1758 | 1764 | } |
|
1759 | 1765 | } |
|
1760 | 1766 | } |
|
1761 | 1767 | } |
|
1762 | 1768 | |
|
1763 | 1769 | #summary{ |
|
1764 | 1770 | width: 70%; |
|
1765 | 1771 | } |
|
1766 | 1772 | |
|
1767 | 1773 | |
|
1768 | 1774 | // Journal |
|
1769 | 1775 | .journal.title { |
|
1770 | 1776 | h5 { |
|
1771 | 1777 | float: left; |
|
1772 | 1778 | margin: 0; |
|
1773 | 1779 | width: 70%; |
|
1774 | 1780 | } |
|
1775 | 1781 | |
|
1776 | 1782 | ul { |
|
1777 | 1783 | float: right; |
|
1778 | 1784 | display: inline-block; |
|
1779 | 1785 | margin: 0; |
|
1780 | 1786 | width: 30%; |
|
1781 | 1787 | text-align: right; |
|
1782 | 1788 | |
|
1783 | 1789 | li { |
|
1784 | 1790 | display: inline; |
|
1785 | 1791 | font-size: @journal-fontsize; |
|
1786 | 1792 | line-height: 1em; |
|
1787 | 1793 | |
|
1788 | 1794 | &:before { content: none; } |
|
1789 | 1795 | } |
|
1790 | 1796 | } |
|
1791 | 1797 | } |
|
1792 | 1798 | |
|
1793 | 1799 | .filterexample { |
|
1794 | 1800 | position: absolute; |
|
1795 | 1801 | top: 95px; |
|
1796 | 1802 | left: @contentpadding; |
|
1797 | 1803 | color: @rcblue; |
|
1798 | 1804 | font-size: 11px; |
|
1799 | 1805 | font-family: @text-regular; |
|
1800 | 1806 | cursor: help; |
|
1801 | 1807 | |
|
1802 | 1808 | &:hover { |
|
1803 | 1809 | color: @rcdarkblue; |
|
1804 | 1810 | } |
|
1805 | 1811 | |
|
1806 | 1812 | @media (max-width:768px) { |
|
1807 | 1813 | position: relative; |
|
1808 | 1814 | top: auto; |
|
1809 | 1815 | left: auto; |
|
1810 | 1816 | display: block; |
|
1811 | 1817 | } |
|
1812 | 1818 | } |
|
1813 | 1819 | |
|
1814 | 1820 | |
|
1815 | 1821 | #journal{ |
|
1816 | 1822 | margin-bottom: @space; |
|
1817 | 1823 | |
|
1818 | 1824 | .journal_day{ |
|
1819 | 1825 | margin-bottom: @textmargin/2; |
|
1820 | 1826 | padding-bottom: @textmargin/2; |
|
1821 | 1827 | font-size: @journal-fontsize; |
|
1822 | 1828 | border-bottom: @border-thickness solid @border-default-color; |
|
1823 | 1829 | } |
|
1824 | 1830 | |
|
1825 | 1831 | .journal_container{ |
|
1826 | 1832 | margin-bottom: @space; |
|
1827 | 1833 | |
|
1828 | 1834 | .journal_user{ |
|
1829 | 1835 | display: inline-block; |
|
1830 | 1836 | } |
|
1831 | 1837 | .journal_action_container{ |
|
1832 | 1838 | display: block; |
|
1833 | 1839 | margin-top: @textmargin; |
|
1834 | 1840 | |
|
1835 | 1841 | div{ |
|
1836 | 1842 | display: inline; |
|
1837 | 1843 | } |
|
1838 | 1844 | |
|
1839 | 1845 | div.journal_action_params{ |
|
1840 | 1846 | display: block; |
|
1841 | 1847 | } |
|
1842 | 1848 | |
|
1843 | 1849 | div.journal_repo:after{ |
|
1844 | 1850 | content: "\A"; |
|
1845 | 1851 | white-space: pre; |
|
1846 | 1852 | } |
|
1847 | 1853 | |
|
1848 | 1854 | div.date{ |
|
1849 | 1855 | display: block; |
|
1850 | 1856 | margin-bottom: @textmargin; |
|
1851 | 1857 | } |
|
1852 | 1858 | } |
|
1853 | 1859 | } |
|
1854 | 1860 | } |
|
1855 | 1861 | |
|
1856 | 1862 | // Files |
|
1857 | 1863 | .edit-file-title { |
|
1858 | 1864 | border-bottom: @border-thickness solid @border-default-color; |
|
1859 | 1865 | |
|
1860 | 1866 | .breadcrumbs { |
|
1861 | 1867 | margin-bottom: 0; |
|
1862 | 1868 | } |
|
1863 | 1869 | } |
|
1864 | 1870 | |
|
1865 | 1871 | .edit-file-fieldset { |
|
1866 | 1872 | margin-top: @sidebarpadding; |
|
1867 | 1873 | |
|
1868 | 1874 | .fieldset { |
|
1869 | 1875 | .left-label { |
|
1870 | 1876 | width: 13%; |
|
1871 | 1877 | } |
|
1872 | 1878 | .right-content { |
|
1873 | 1879 | width: 87%; |
|
1874 | 1880 | max-width: 100%; |
|
1875 | 1881 | } |
|
1876 | 1882 | .filename-label { |
|
1877 | 1883 | margin-top: 13px; |
|
1878 | 1884 | } |
|
1879 | 1885 | .commit-message-label { |
|
1880 | 1886 | margin-top: 4px; |
|
1881 | 1887 | } |
|
1882 | 1888 | .file-upload-input { |
|
1883 | 1889 | input { |
|
1884 | 1890 | display: none; |
|
1885 | 1891 | } |
|
1886 | 1892 | } |
|
1887 | 1893 | p { |
|
1888 | 1894 | margin-top: 5px; |
|
1889 | 1895 | } |
|
1890 | 1896 | |
|
1891 | 1897 | } |
|
1892 | 1898 | .custom-path-link { |
|
1893 | 1899 | margin-left: 5px; |
|
1894 | 1900 | } |
|
1895 | 1901 | #commit { |
|
1896 | 1902 | resize: vertical; |
|
1897 | 1903 | } |
|
1898 | 1904 | } |
|
1899 | 1905 | |
|
1900 | 1906 | .delete-file-preview { |
|
1901 | 1907 | max-height: 250px; |
|
1902 | 1908 | } |
|
1903 | 1909 | |
|
1904 | 1910 | .new-file, |
|
1905 | 1911 | #filter_activate, |
|
1906 | 1912 | #filter_deactivate { |
|
1907 | 1913 | float: left; |
|
1908 | 1914 | margin: 0 0 0 15px; |
|
1909 | 1915 | } |
|
1910 | 1916 | |
|
1911 | 1917 | h3.files_location{ |
|
1912 | 1918 | line-height: 2.4em; |
|
1913 | 1919 | } |
|
1914 | 1920 | |
|
1915 | 1921 | .browser-nav { |
|
1916 | 1922 | display: table; |
|
1917 | 1923 | margin-bottom: @space; |
|
1918 | 1924 | |
|
1919 | 1925 | |
|
1920 | 1926 | .info_box { |
|
1921 | 1927 | display: inline-table; |
|
1922 | 1928 | height: 2.5em; |
|
1923 | 1929 | |
|
1924 | 1930 | .browser-cur-rev, .info_box_elem { |
|
1925 | 1931 | display: table-cell; |
|
1926 | 1932 | vertical-align: middle; |
|
1927 | 1933 | } |
|
1928 | 1934 | |
|
1929 | 1935 | .info_box_elem { |
|
1930 | 1936 | border-top: @border-thickness solid @rcblue; |
|
1931 | 1937 | border-bottom: @border-thickness solid @rcblue; |
|
1932 | 1938 | |
|
1933 | 1939 | #at_rev, a { |
|
1934 | 1940 | padding: 0.6em 0.9em; |
|
1935 | 1941 | margin: 0; |
|
1936 | 1942 | .box-shadow(none); |
|
1937 | 1943 | border: 0; |
|
1938 | 1944 | height: 12px; |
|
1939 | 1945 | } |
|
1940 | 1946 | |
|
1941 | 1947 | input#at_rev { |
|
1942 | 1948 | max-width: 50px; |
|
1943 | 1949 | text-align: right; |
|
1944 | 1950 | } |
|
1945 | 1951 | |
|
1946 | 1952 | &.previous { |
|
1947 | 1953 | border: @border-thickness solid @rcblue; |
|
1948 | 1954 | .disabled { |
|
1949 | 1955 | color: @grey4; |
|
1950 | 1956 | cursor: not-allowed; |
|
1951 | 1957 | } |
|
1952 | 1958 | } |
|
1953 | 1959 | |
|
1954 | 1960 | &.next { |
|
1955 | 1961 | border: @border-thickness solid @rcblue; |
|
1956 | 1962 | .disabled { |
|
1957 | 1963 | color: @grey4; |
|
1958 | 1964 | cursor: not-allowed; |
|
1959 | 1965 | } |
|
1960 | 1966 | } |
|
1961 | 1967 | } |
|
1962 | 1968 | |
|
1963 | 1969 | .browser-cur-rev { |
|
1964 | 1970 | |
|
1965 | 1971 | span{ |
|
1966 | 1972 | margin: 0; |
|
1967 | 1973 | color: @rcblue; |
|
1968 | 1974 | height: 12px; |
|
1969 | 1975 | display: inline-block; |
|
1970 | 1976 | padding: 0.7em 1em ; |
|
1971 | 1977 | border: @border-thickness solid @rcblue; |
|
1972 | 1978 | margin-right: @padding; |
|
1973 | 1979 | } |
|
1974 | 1980 | } |
|
1975 | 1981 | } |
|
1976 | 1982 | |
|
1977 | 1983 | .search_activate { |
|
1978 | 1984 | display: table-cell; |
|
1979 | 1985 | vertical-align: middle; |
|
1980 | 1986 | |
|
1981 | 1987 | input, label{ |
|
1982 | 1988 | margin: 0; |
|
1983 | 1989 | padding: 0; |
|
1984 | 1990 | } |
|
1985 | 1991 | |
|
1986 | 1992 | input{ |
|
1987 | 1993 | margin-left: @textmargin; |
|
1988 | 1994 | } |
|
1989 | 1995 | |
|
1990 | 1996 | } |
|
1991 | 1997 | } |
|
1992 | 1998 | |
|
1993 | 1999 | .browser-cur-rev{ |
|
1994 | 2000 | margin-bottom: @textmargin; |
|
1995 | 2001 | } |
|
1996 | 2002 | |
|
1997 | 2003 | #node_filter_box_loading{ |
|
1998 | 2004 | .info_text; |
|
1999 | 2005 | } |
|
2000 | 2006 | |
|
2001 | 2007 | .browser-search { |
|
2002 | 2008 | margin: -25px 0px 5px 0px; |
|
2003 | 2009 | } |
|
2004 | 2010 | |
|
2005 | 2011 | .node-filter { |
|
2006 | 2012 | font-size: @repo-title-fontsize; |
|
2007 | 2013 | padding: 4px 0px 0px 0px; |
|
2008 | 2014 | |
|
2009 | 2015 | .node-filter-path { |
|
2010 | 2016 | float: left; |
|
2011 | 2017 | color: @grey4; |
|
2012 | 2018 | } |
|
2013 | 2019 | .node-filter-input { |
|
2014 | 2020 | float: left; |
|
2015 | 2021 | margin: -2px 0px 0px 2px; |
|
2016 | 2022 | input { |
|
2017 | 2023 | padding: 2px; |
|
2018 | 2024 | border: none; |
|
2019 | 2025 | font-size: @repo-title-fontsize; |
|
2020 | 2026 | } |
|
2021 | 2027 | } |
|
2022 | 2028 | } |
|
2023 | 2029 | |
|
2024 | 2030 | |
|
2025 | 2031 | .browser-result{ |
|
2026 | 2032 | td a{ |
|
2027 | 2033 | margin-left: 0.5em; |
|
2028 | 2034 | display: inline-block; |
|
2029 | 2035 | |
|
2030 | 2036 | em{ |
|
2031 | 2037 | font-family: @text-bold; |
|
2032 | 2038 | } |
|
2033 | 2039 | } |
|
2034 | 2040 | } |
|
2035 | 2041 | |
|
2036 | 2042 | .browser-highlight{ |
|
2037 | 2043 | background-color: @grey5-alpha; |
|
2038 | 2044 | } |
|
2039 | 2045 | |
|
2040 | 2046 | |
|
2041 | 2047 | // Search |
|
2042 | 2048 | |
|
2043 | 2049 | .search-form{ |
|
2044 | 2050 | #q { |
|
2045 | 2051 | width: @search-form-width; |
|
2046 | 2052 | } |
|
2047 | 2053 | .fields{ |
|
2048 | 2054 | margin: 0 0 @space; |
|
2049 | 2055 | } |
|
2050 | 2056 | |
|
2051 | 2057 | label{ |
|
2052 | 2058 | display: inline-block; |
|
2053 | 2059 | margin-right: @textmargin; |
|
2054 | 2060 | padding-top: 0.25em; |
|
2055 | 2061 | } |
|
2056 | 2062 | |
|
2057 | 2063 | |
|
2058 | 2064 | .results{ |
|
2059 | 2065 | clear: both; |
|
2060 | 2066 | margin: 0 0 @padding; |
|
2061 | 2067 | } |
|
2062 | 2068 | } |
|
2063 | 2069 | |
|
2064 | 2070 | div.search-feedback-items { |
|
2065 | 2071 | display: inline-block; |
|
2066 | 2072 | padding:0px 0px 0px 96px; |
|
2067 | 2073 | } |
|
2068 | 2074 | |
|
2069 | 2075 | div.search-code-body { |
|
2070 | 2076 | background-color: #ffffff; padding: 5px 0 5px 10px; |
|
2071 | 2077 | pre { |
|
2072 | 2078 | .match { background-color: #faffa6;} |
|
2073 | 2079 | .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; } |
|
2074 | 2080 | } |
|
2075 | 2081 | } |
|
2076 | 2082 | |
|
2077 | 2083 | .expand_commit.search { |
|
2078 | 2084 | .show_more.open { |
|
2079 | 2085 | height: auto; |
|
2080 | 2086 | max-height: none; |
|
2081 | 2087 | } |
|
2082 | 2088 | } |
|
2083 | 2089 | |
|
2084 | 2090 | .search-results { |
|
2085 | 2091 | |
|
2086 | 2092 | h2 { |
|
2087 | 2093 | margin-bottom: 0; |
|
2088 | 2094 | } |
|
2089 | 2095 | .codeblock { |
|
2090 | 2096 | border: none; |
|
2091 | 2097 | background: transparent; |
|
2092 | 2098 | } |
|
2093 | 2099 | |
|
2094 | 2100 | .codeblock-header { |
|
2095 | 2101 | border: none; |
|
2096 | 2102 | background: transparent; |
|
2097 | 2103 | } |
|
2098 | 2104 | |
|
2099 | 2105 | .code-body { |
|
2100 | 2106 | border: @border-thickness solid @border-default-color; |
|
2101 | 2107 | .border-radius(@border-radius); |
|
2102 | 2108 | } |
|
2103 | 2109 | |
|
2104 | 2110 | .td-commit { |
|
2105 | 2111 | &:extend(pre); |
|
2106 | 2112 | border-bottom: @border-thickness solid @border-default-color; |
|
2107 | 2113 | } |
|
2108 | 2114 | |
|
2109 | 2115 | .message { |
|
2110 | 2116 | height: auto; |
|
2111 | 2117 | max-width: 350px; |
|
2112 | 2118 | white-space: normal; |
|
2113 | 2119 | text-overflow: initial; |
|
2114 | 2120 | overflow: visible; |
|
2115 | 2121 | |
|
2116 | 2122 | .match { background-color: #faffa6;} |
|
2117 | 2123 | .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; } |
|
2118 | 2124 | } |
|
2119 | 2125 | |
|
2120 | 2126 | } |
|
2121 | 2127 | |
|
2122 | 2128 | table.rctable td.td-search-results div { |
|
2123 | 2129 | max-width: 100%; |
|
2124 | 2130 | } |
|
2125 | 2131 | |
|
2126 | 2132 | #tip-box, .tip-box{ |
|
2127 | 2133 | padding: @menupadding/2; |
|
2128 | 2134 | display: block; |
|
2129 | 2135 | border: @border-thickness solid @border-highlight-color; |
|
2130 | 2136 | .border-radius(@border-radius); |
|
2131 | 2137 | background-color: white; |
|
2132 | 2138 | z-index: 99; |
|
2133 | 2139 | white-space: pre-wrap; |
|
2134 | 2140 | } |
|
2135 | 2141 | |
|
2136 | 2142 | #linktt { |
|
2137 | 2143 | width: 79px; |
|
2138 | 2144 | } |
|
2139 | 2145 | |
|
2140 | 2146 | #help_kb .modal-content{ |
|
2141 | 2147 | max-width: 750px; |
|
2142 | 2148 | margin: 10% auto; |
|
2143 | 2149 | |
|
2144 | 2150 | table{ |
|
2145 | 2151 | td,th{ |
|
2146 | 2152 | border-bottom: none; |
|
2147 | 2153 | line-height: 2.5em; |
|
2148 | 2154 | } |
|
2149 | 2155 | th{ |
|
2150 | 2156 | padding-bottom: @textmargin/2; |
|
2151 | 2157 | } |
|
2152 | 2158 | td.keys{ |
|
2153 | 2159 | text-align: center; |
|
2154 | 2160 | } |
|
2155 | 2161 | } |
|
2156 | 2162 | |
|
2157 | 2163 | .block-left{ |
|
2158 | 2164 | width: 45%; |
|
2159 | 2165 | margin-right: 5%; |
|
2160 | 2166 | } |
|
2161 | 2167 | .modal-footer{ |
|
2162 | 2168 | clear: both; |
|
2163 | 2169 | } |
|
2164 | 2170 | .key.tag{ |
|
2165 | 2171 | padding: 0.5em; |
|
2166 | 2172 | background-color: @rcblue; |
|
2167 | 2173 | color: white; |
|
2168 | 2174 | border-color: @rcblue; |
|
2169 | 2175 | .box-shadow(none); |
|
2170 | 2176 | } |
|
2171 | 2177 | } |
|
2172 | 2178 | |
|
2173 | 2179 | |
|
2174 | 2180 | |
|
2175 | 2181 | //--- IMPORTS FOR REFACTORED STYLES ------------------// |
|
2176 | 2182 | |
|
2177 | 2183 | @import 'statistics-graph'; |
|
2178 | 2184 | @import 'tables'; |
|
2179 | 2185 | @import 'forms'; |
|
2180 | 2186 | @import 'diff'; |
|
2181 | 2187 | @import 'summary'; |
|
2182 | 2188 | @import 'navigation'; |
|
2183 | 2189 | |
|
2184 | 2190 | //--- SHOW/HIDE SECTIONS --// |
|
2185 | 2191 | |
|
2186 | 2192 | .btn-collapse { |
|
2187 | 2193 | float: right; |
|
2188 | 2194 | text-align: right; |
|
2189 | 2195 | font-family: @text-light; |
|
2190 | 2196 | font-size: @basefontsize; |
|
2191 | 2197 | cursor: pointer; |
|
2192 | 2198 | border: none; |
|
2193 | 2199 | color: @rcblue; |
|
2194 | 2200 | } |
|
2195 | 2201 | |
|
2196 | 2202 | table.rctable, |
|
2197 | 2203 | table.dataTable { |
|
2198 | 2204 | .btn-collapse { |
|
2199 | 2205 | float: right; |
|
2200 | 2206 | text-align: right; |
|
2201 | 2207 | } |
|
2202 | 2208 | } |
|
2203 | 2209 | |
|
2204 | 2210 | |
|
2205 | 2211 | // TODO: johbo: Fix for IE10, this avoids that we see a border |
|
2206 | 2212 | // and padding around checkboxes and radio boxes. Move to the right place, |
|
2207 | 2213 | // or better: Remove this once we did the form refactoring. |
|
2208 | 2214 | input[type=checkbox], |
|
2209 | 2215 | input[type=radio] { |
|
2210 | 2216 | padding: 0; |
|
2211 | 2217 | border: none; |
|
2212 | 2218 | } |
|
2213 | 2219 | |
|
2214 | 2220 | .toggle-ajax-spinner{ |
|
2215 | 2221 | height: 16px; |
|
2216 | 2222 | width: 16px; |
|
2217 | 2223 | } |
@@ -1,530 +1,541 b'' | |||
|
1 | 1 | |
|
2 | 2 | // tables.less |
|
3 | 3 | // For use in RhodeCode application tables; |
|
4 | 4 | // see style guide documentation for guidelines. |
|
5 | 5 | |
|
6 | 6 | // TABLES |
|
7 | 7 | |
|
8 | 8 | .rctable, |
|
9 | 9 | table.rctable, |
|
10 | 10 | table.dataTable { |
|
11 | 11 | clear:both; |
|
12 | 12 | width: 100%; |
|
13 | 13 | margin: 0 auto @padding; |
|
14 | 14 | padding: 0; |
|
15 | 15 | vertical-align: baseline; |
|
16 | 16 | line-height:1.5em; |
|
17 | 17 | border: none; |
|
18 | 18 | outline: none; |
|
19 | 19 | border-collapse: collapse; |
|
20 | 20 | border-spacing: 0; |
|
21 | 21 | color: @grey2; |
|
22 | 22 | |
|
23 | 23 | b { |
|
24 | 24 | font-weight: normal; |
|
25 | 25 | } |
|
26 | 26 | |
|
27 | 27 | em { |
|
28 | 28 | font-weight: bold; |
|
29 | 29 | font-style: normal; |
|
30 | 30 | } |
|
31 | 31 | |
|
32 | 32 | th, |
|
33 | 33 | td { |
|
34 | 34 | height: auto; |
|
35 | 35 | max-width: 20%; |
|
36 | 36 | padding: .65em 1em .65em 0; |
|
37 | 37 | vertical-align: middle; |
|
38 | 38 | border-bottom: @border-thickness solid @grey5; |
|
39 | 39 | white-space: normal; |
|
40 | 40 | |
|
41 | 41 | &.td-radio, |
|
42 | 42 | &.td-checkbox { |
|
43 | 43 | padding-right: 0; |
|
44 | 44 | text-align: center; |
|
45 | 45 | |
|
46 | 46 | input { |
|
47 | 47 | margin: 0 1em; |
|
48 | 48 | } |
|
49 | 49 | } |
|
50 | 50 | |
|
51 | 51 | &.truncate-wrap { |
|
52 | 52 | white-space: nowrap !important; |
|
53 | 53 | } |
|
54 | 54 | |
|
55 | 55 | pre { |
|
56 | 56 | margin: 0; |
|
57 | 57 | } |
|
58 | 58 | |
|
59 | 59 | .show_more { |
|
60 | 60 | height: inherit; |
|
61 | 61 | } |
|
62 | 62 | } |
|
63 | 63 | |
|
64 | 64 | .expired td { |
|
65 | 65 | background-color: @grey7; |
|
66 | 66 | } |
|
67 | 67 | |
|
68 | 68 | .td-radio + .td-owner { |
|
69 | 69 | padding-left: 1em; |
|
70 | 70 | } |
|
71 | 71 | |
|
72 | 72 | |
|
73 | 73 | th { |
|
74 | 74 | text-align: left; |
|
75 | 75 | font-family: @text-semibold; |
|
76 | 76 | } |
|
77 | 77 | |
|
78 | 78 | .hl { |
|
79 | 79 | td { |
|
80 | 80 | background-color: lighten(@alert4,25%); |
|
81 | 81 | } |
|
82 | 82 | } |
|
83 | 83 | |
|
84 | 84 | // Special Data Cell Types |
|
85 | 85 | // See style guide for desciptions and examples. |
|
86 | 86 | |
|
87 | 87 | td { |
|
88 | 88 | |
|
89 | 89 | &.user { |
|
90 | 90 | padding-left: 1em; |
|
91 | 91 | } |
|
92 | 92 | |
|
93 | 93 | &.td-rss { |
|
94 | 94 | width: 20px; |
|
95 | 95 | min-width: 0; |
|
96 | 96 | margin: 0; |
|
97 | 97 | } |
|
98 | 98 | |
|
99 | 99 | &.quick_repo_menu { |
|
100 | 100 | width: 15px; |
|
101 | 101 | text-align: center; |
|
102 | 102 | |
|
103 | 103 | &:hover { |
|
104 | 104 | background-color: @grey5; |
|
105 | 105 | } |
|
106 | 106 | } |
|
107 | 107 | |
|
108 | 108 | &.td-hash { |
|
109 | 109 | min-width: 80px; |
|
110 | 110 | width: 200px; |
|
111 | 111 | } |
|
112 | 112 | |
|
113 | 113 | &.td-time { |
|
114 | 114 | width: 160px; |
|
115 | 115 | white-space: nowrap; |
|
116 | 116 | } |
|
117 | 117 | |
|
118 | 118 | &.annotate{ |
|
119 | 119 | padding-right: 0; |
|
120 | 120 | |
|
121 | 121 | div.annotatediv{ |
|
122 | 122 | margin: 0 0.7em; |
|
123 | 123 | } |
|
124 | 124 | } |
|
125 | 125 | |
|
126 | 126 | &.tags-col { |
|
127 | 127 | padding-right: 0; |
|
128 | 128 | } |
|
129 | 129 | |
|
130 | 130 | &.td-description { |
|
131 | 131 | min-width: 350px; |
|
132 | 132 | |
|
133 | 133 | &.truncate, .truncate-wrap { |
|
134 | 134 | white-space: nowrap; |
|
135 | 135 | overflow: hidden; |
|
136 | 136 | text-overflow: ellipsis; |
|
137 | 137 | max-width: 450px; |
|
138 | 138 | } |
|
139 | 139 | } |
|
140 | 140 | |
|
141 | 141 | &.td-componentname { |
|
142 | 142 | white-space: nowrap; |
|
143 | 143 | } |
|
144 | 144 | |
|
145 | 145 | &.td-journalaction { |
|
146 | 146 | min-width: 300px; |
|
147 | 147 | |
|
148 | 148 | .journal_action_params { |
|
149 | 149 | // waiting for feedback |
|
150 | 150 | } |
|
151 | 151 | } |
|
152 | 152 | |
|
153 | 153 | &.td-active { |
|
154 | 154 | padding-left: .65em; |
|
155 | 155 | } |
|
156 | 156 | |
|
157 | 157 | &.td-url { |
|
158 | 158 | white-space: nowrap; |
|
159 | 159 | } |
|
160 | 160 | |
|
161 | 161 | &.td-comments { |
|
162 | 162 | min-width: 3em; |
|
163 | 163 | } |
|
164 | 164 | |
|
165 | 165 | &.td-buttons { |
|
166 | 166 | padding: .3em 0; |
|
167 | 167 | } |
|
168 | 168 | |
|
169 | 169 | &.td-action { |
|
170 | 170 | // this is for the remove/delete/edit buttons |
|
171 | 171 | padding-right: 0; |
|
172 | 172 | min-width: 95px; |
|
173 | 173 | text-transform: capitalize; |
|
174 | 174 | |
|
175 | 175 | i { |
|
176 | 176 | display: none; |
|
177 | 177 | } |
|
178 | 178 | } |
|
179 | 179 | |
|
180 | 180 | // TODO: lisa: this needs to be cleaned up with the buttons |
|
181 | 181 | .grid_edit, |
|
182 | 182 | .grid_delete { |
|
183 | 183 | display: inline-block; |
|
184 | 184 | margin: 0 @padding/3 0 0; |
|
185 | 185 | font-family: @text-light; |
|
186 | 186 | |
|
187 | 187 | i { |
|
188 | 188 | display: none; |
|
189 | 189 | } |
|
190 | 190 | } |
|
191 | 191 | |
|
192 | 192 | .grid_edit + .grid_delete { |
|
193 | 193 | border-left: @border-thickness solid @grey5; |
|
194 | 194 | padding-left: @padding/2; |
|
195 | 195 | } |
|
196 | 196 | |
|
197 | 197 | &.td-compare { |
|
198 | 198 | |
|
199 | 199 | input { |
|
200 | 200 | margin-right: 1em; |
|
201 | 201 | } |
|
202 | 202 | |
|
203 | 203 | .compare-radio-button { |
|
204 | 204 | margin: 0 1em 0 0; |
|
205 | 205 | } |
|
206 | 206 | |
|
207 | 207 | |
|
208 | 208 | } |
|
209 | 209 | |
|
210 | 210 | &.td-tags { |
|
211 | 211 | padding: .5em 1em .5em 0; |
|
212 | 212 | width: 140px; |
|
213 | 213 | |
|
214 | 214 | .tag { |
|
215 | 215 | margin: 1px; |
|
216 | 216 | float: left; |
|
217 | 217 | } |
|
218 | 218 | } |
|
219 | 219 | |
|
220 | 220 | .icon-svn, .icon-hg, .icon-git { |
|
221 | 221 | font-size: 1.4em; |
|
222 | 222 | } |
|
223 | 223 | |
|
224 | 224 | &.collapse_commit, |
|
225 | 225 | &.expand_commit { |
|
226 | 226 | padding-right: 0; |
|
227 | 227 | padding-left: 1em; |
|
228 | 228 | } |
|
229 | 229 | } |
|
230 | 230 | |
|
231 | 231 | .perm_admin_row { |
|
232 | 232 | color: @grey4; |
|
233 | 233 | background-color: @grey6; |
|
234 | 234 | } |
|
235 | 235 | |
|
236 | 236 | .noborder { |
|
237 | 237 | border: none; |
|
238 | 238 | |
|
239 | 239 | td { |
|
240 | 240 | border: none; |
|
241 | 241 | } |
|
242 | 242 | } |
|
243 | 243 | } |
|
244 | 244 | |
|
245 | 245 | // TRUNCATING |
|
246 | 246 | // TODO: lisaq: should this possibly be moved out of tables.less? |
|
247 | 247 | // for truncated text |
|
248 | 248 | // used inside of table cells and in code block headers |
|
249 | 249 | .truncate-wrap { |
|
250 | 250 | white-space: nowrap !important; |
|
251 | 251 | |
|
252 | 252 | //truncated text |
|
253 | 253 | .truncate { |
|
254 | 254 | max-width: 450px; |
|
255 | 255 | width: 300px; |
|
256 | 256 | overflow: hidden; |
|
257 | 257 | text-overflow: ellipsis; |
|
258 | 258 | -o-text-overflow: ellipsis; |
|
259 | 259 | -ms-text-overflow: ellipsis; |
|
260 | 260 | |
|
261 | 261 | &.autoexpand { |
|
262 | 262 | width: 120px; |
|
263 | 263 | margin-right: 200px; |
|
264 | 264 | } |
|
265 | 265 | } |
|
266 | 266 | &:hover .truncate.autoexpand { |
|
267 | 267 | overflow: visible; |
|
268 | 268 | } |
|
269 | 269 | |
|
270 | 270 | .tags-truncate { |
|
271 | 271 | width: 150px; |
|
272 | 272 | height: 22px; |
|
273 | 273 | overflow: hidden; |
|
274 | 274 | |
|
275 | 275 | .tag { |
|
276 | 276 | display: inline-block; |
|
277 | 277 | } |
|
278 | 278 | |
|
279 | 279 | &.truncate { |
|
280 | 280 | height: 22px; |
|
281 | 281 | max-height:2em; |
|
282 | 282 | width: 140px; |
|
283 | 283 | } |
|
284 | 284 | } |
|
285 | 285 | } |
|
286 | 286 | |
|
287 | 287 | .apikeys_wrap { |
|
288 | 288 | margin-bottom: @padding; |
|
289 | 289 | |
|
290 | 290 | table.rctable td:first-child { |
|
291 | 291 | width: 340px; |
|
292 | 292 | } |
|
293 | 293 | } |
|
294 | 294 | |
|
295 | 295 | |
|
296 | 296 | |
|
297 | 297 | // SPECIAL CASES |
|
298 | 298 | |
|
299 | 299 | // Repository Followers |
|
300 | 300 | table.rctable.followers_data { |
|
301 | 301 | width: 75%; |
|
302 | 302 | margin: 0; |
|
303 | 303 | } |
|
304 | 304 | |
|
305 | 305 | // Repository List |
|
306 | 306 | // Group Members List |
|
307 | 307 | table.rctable.group_members, |
|
308 | 308 | table#repo_list_table { |
|
309 | 309 | min-width: 600px; |
|
310 | 310 | } |
|
311 | 311 | |
|
312 | 312 | // Keyboard mappings |
|
313 | 313 | table.keyboard-mappings { |
|
314 | 314 | th { |
|
315 | 315 | text-align: left; |
|
316 | 316 | font-family: @text-semibold; |
|
317 | 317 | } |
|
318 | 318 | } |
|
319 | 319 | |
|
320 | 320 | // Branches, Tags, and Bookmarks |
|
321 | 321 | #obj_list_table.dataTable { |
|
322 | 322 | td.td-time { |
|
323 | 323 | padding-right: 1em; |
|
324 | 324 | } |
|
325 | 325 | } |
|
326 | 326 | |
|
327 | 327 | // User Admin |
|
328 | 328 | .rctable.useremails, |
|
329 | 329 | .rctable.account_emails { |
|
330 | 330 | .tag, |
|
331 | 331 | .btn { |
|
332 | 332 | float: right; |
|
333 | 333 | } |
|
334 | 334 | .btn { //to line up with tags |
|
335 | 335 | margin-right: 1.65em; |
|
336 | 336 | } |
|
337 | 337 | } |
|
338 | 338 | |
|
339 | 339 | // User List |
|
340 | 340 | #user_list_table { |
|
341 | 341 | |
|
342 | 342 | td.td-user { |
|
343 | 343 | min-width: 100px; |
|
344 | 344 | } |
|
345 | 345 | } |
|
346 | 346 | |
|
347 | 347 | // Pull Request List Table |
|
348 | 348 | #pull_request_list_table.dataTable { |
|
349 | 349 | |
|
350 | 350 | //TODO: lisa: This needs to be removed once the description is adjusted |
|
351 | 351 | // for using an expand_commit button (see issue 765) |
|
352 | 352 | td { |
|
353 | 353 | vertical-align: middle; |
|
354 | 354 | } |
|
355 | 355 | } |
|
356 | 356 | |
|
357 | 357 | // Settings (no border) |
|
358 | 358 | table.rctable.dl-settings { |
|
359 | 359 | td { |
|
360 | 360 | border: none; |
|
361 | 361 | } |
|
362 | 362 | } |
|
363 | 363 | |
|
364 | 364 | |
|
365 | 365 | // Statistics |
|
366 | 366 | table.trending_language_tbl { |
|
367 | 367 | width: 100%; |
|
368 | 368 | line-height: 1em; |
|
369 | 369 | |
|
370 | 370 | td div { |
|
371 | 371 | overflow: visible; |
|
372 | 372 | } |
|
373 | 373 | } |
|
374 | 374 | |
|
375 | 375 | .trending_language_tbl, .trending_language_tbl td { |
|
376 | 376 | border: 0; |
|
377 | 377 | margin: 0; |
|
378 | 378 | padding: 0; |
|
379 | 379 | background: transparent; |
|
380 | 380 | } |
|
381 | 381 | |
|
382 | 382 | .trending_language_tbl, .trending_language_tbl tr { |
|
383 | 383 | border-spacing: 0 3px; |
|
384 | 384 | } |
|
385 | 385 | |
|
386 | 386 | .trending_language { |
|
387 | 387 | position: relative; |
|
388 | 388 | width: 100%; |
|
389 | 389 | height: 19px; |
|
390 | 390 | overflow: hidden; |
|
391 | 391 | background-color: @grey6; |
|
392 | 392 | |
|
393 | 393 | span, b{ |
|
394 | 394 | position: absolute; |
|
395 | 395 | display: block; |
|
396 | 396 | height: 12px; |
|
397 | 397 | margin-bottom: 0px; |
|
398 | 398 | white-space: pre; |
|
399 | 399 | padding: floor(@basefontsize/4); |
|
400 | 400 | top: 0; |
|
401 | 401 | left: 0; |
|
402 | 402 | } |
|
403 | 403 | |
|
404 | 404 | span{ |
|
405 | 405 | color: @text-color; |
|
406 | 406 | z-index: 0; |
|
407 | 407 | min-width: 20px; |
|
408 | 408 | } |
|
409 | 409 | |
|
410 | 410 | b { |
|
411 | 411 | z-index: 1; |
|
412 | 412 | overflow: hidden; |
|
413 | 413 | background-color: @rcblue; |
|
414 | 414 | color: #FFF; |
|
415 | 415 | text-decoration: none; |
|
416 | 416 | } |
|
417 | 417 | |
|
418 | 418 | } |
|
419 | 419 | |
|
420 | 420 | // Changesets |
|
421 | 421 | #changesets.rctable { |
|
422 | 422 | |
|
423 | 423 | // td must be fixed height for graph |
|
424 | 424 | td { |
|
425 | 425 | height: 32px; |
|
426 | 426 | padding: 0 1em 0 0; |
|
427 | 427 | vertical-align: middle; |
|
428 | 428 | white-space: nowrap; |
|
429 | 429 | |
|
430 | 430 | &.td-description { |
|
431 | 431 | white-space: normal; |
|
432 | 432 | } |
|
433 | 433 | |
|
434 | 434 | &.expand_commit { |
|
435 | 435 | padding-right: 0; |
|
436 | 436 | } |
|
437 | 437 | } |
|
438 | 438 | } |
|
439 | 439 | |
|
440 | 440 | // Compare |
|
441 | 441 | table.compare_view_commits { |
|
442 | 442 | margin-top: @space; |
|
443 | 443 | |
|
444 | 444 | td.td-time { |
|
445 | 445 | padding-left: .5em; |
|
446 | 446 | } |
|
447 | 447 | |
|
448 | // special case to not show hover actions on hidden indicator | |
|
449 | tr.compare_select_hidden:hover { | |
|
450 | cursor: inherit; | |
|
451 | ||
|
452 | td { | |
|
453 | background-color: inherit; | |
|
454 | } | |
|
455 | } | |
|
456 | ||
|
448 | 457 | tr:hover { |
|
449 | 458 | cursor: pointer; |
|
450 | 459 | |
|
451 | 460 | td { |
|
452 | 461 | background-color: lighten(@alert4,25%); |
|
453 | 462 | } |
|
454 | 463 | } |
|
464 | ||
|
465 | ||
|
455 | 466 | } |
|
456 | 467 | |
|
457 | 468 | .file_history { |
|
458 | 469 | td.td-actions { |
|
459 | 470 | text-align: right; |
|
460 | 471 | } |
|
461 | 472 | } |
|
462 | 473 | |
|
463 | 474 | .compare_view_files { |
|
464 | 475 | |
|
465 | 476 | td.td-actions { |
|
466 | 477 | text-align: right; |
|
467 | 478 | } |
|
468 | 479 | |
|
469 | 480 | .flag_status { |
|
470 | 481 | margin: 0 0 0 5px; |
|
471 | 482 | } |
|
472 | 483 | |
|
473 | 484 | td.injected_diff { |
|
474 | 485 | |
|
475 | 486 | .code-difftable { |
|
476 | 487 | border:none; |
|
477 | 488 | } |
|
478 | 489 | |
|
479 | 490 | .diff-container { |
|
480 | 491 | border: @border-thickness solid @border-default-color; |
|
481 | 492 | .border-radius(@border-radius); |
|
482 | 493 | } |
|
483 | 494 | |
|
484 | 495 | div.diffblock { |
|
485 | 496 | border:none; |
|
486 | 497 | } |
|
487 | 498 | |
|
488 | 499 | div.code-body { |
|
489 | 500 | max-width: 1152px; |
|
490 | 501 | } |
|
491 | 502 | } |
|
492 | 503 | |
|
493 | 504 | .rctable { |
|
494 | 505 | |
|
495 | 506 | td { |
|
496 | 507 | padding-top: @space; |
|
497 | 508 | } |
|
498 | 509 | |
|
499 | 510 | &:first-child td { |
|
500 | 511 | padding-top: 0; |
|
501 | 512 | } |
|
502 | 513 | } |
|
503 | 514 | |
|
504 | 515 | .comment-bubble, |
|
505 | 516 | .show_comments { |
|
506 | 517 | float: right; |
|
507 | 518 | visibility: hidden; |
|
508 | 519 | padding: 0 1em 0 0; |
|
509 | 520 | } |
|
510 | 521 | |
|
511 | 522 | .injected_diff { |
|
512 | 523 | padding-bottom: @padding; |
|
513 | 524 | } |
|
514 | 525 | } |
|
515 | 526 | |
|
516 | 527 | // Gist List |
|
517 | 528 | #gist_list_table { |
|
518 | 529 | td { |
|
519 | 530 | vertical-align: middle; |
|
520 | 531 | |
|
521 | 532 | div{ |
|
522 | 533 | display: inline-block; |
|
523 | 534 | vertical-align: middle; |
|
524 | 535 | } |
|
525 | 536 | |
|
526 | 537 | img{ |
|
527 | 538 | vertical-align: middle; |
|
528 | 539 | } |
|
529 | 540 | } |
|
530 | 541 | } |
@@ -1,640 +1,651 b'' | |||
|
1 | 1 | // # Copyright (C) 2010-2016 RhodeCode GmbH |
|
2 | 2 | // # |
|
3 | 3 | // # This program is free software: you can redistribute it and/or modify |
|
4 | 4 | // # it under the terms of the GNU Affero General Public License, version 3 |
|
5 | 5 | // # (only), as published by the Free Software Foundation. |
|
6 | 6 | // # |
|
7 | 7 | // # This program is distributed in the hope that it will be useful, |
|
8 | 8 | // # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
9 | 9 | // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
10 | 10 | // # GNU General Public License for more details. |
|
11 | 11 | // # |
|
12 | 12 | // # You should have received a copy of the GNU Affero General Public License |
|
13 | 13 | // # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
14 | 14 | // # |
|
15 | 15 | // # This program is dual-licensed. If you wish to learn more about the |
|
16 | 16 | // # RhodeCode Enterprise Edition, including its added features, Support services, |
|
17 | 17 | // # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
18 | 18 | |
|
19 | 19 | var firefoxAnchorFix = function() { |
|
20 | 20 | // hack to make anchor links behave properly on firefox, in our inline |
|
21 | 21 | // comments generation when comments are injected firefox is misbehaving |
|
22 | 22 | // when jumping to anchor links |
|
23 | 23 | if (location.href.indexOf('#') > -1) { |
|
24 | 24 | location.href += ''; |
|
25 | 25 | } |
|
26 | 26 | }; |
|
27 | 27 | |
|
28 | 28 | // returns a node from given html; |
|
29 | 29 | var fromHTML = function(html){ |
|
30 | 30 | var _html = document.createElement('element'); |
|
31 | 31 | _html.innerHTML = html; |
|
32 | 32 | return _html; |
|
33 | 33 | }; |
|
34 | 34 | |
|
35 | 35 | var tableTr = function(cls, body){ |
|
36 | 36 | var _el = document.createElement('div'); |
|
37 | 37 | var _body = $(body).attr('id'); |
|
38 | 38 | var comment_id = fromHTML(body).children[0].id.split('comment-')[1]; |
|
39 | 39 | var id = 'comment-tr-{0}'.format(comment_id); |
|
40 | 40 | var _html = ('<table><tbody><tr id="{0}" class="{1}">'+ |
|
41 | 41 | '<td class="add-comment-line tooltip tooltip" title="Add Comment"><span class="add-comment-content"></span></td>'+ |
|
42 | 42 | '<td></td>'+ |
|
43 | 43 | '<td></td>'+ |
|
44 | 44 | '<td></td>'+ |
|
45 | 45 | '<td>{2}</td>'+ |
|
46 | 46 | '</tr></tbody></table>').format(id, cls, body); |
|
47 | 47 | $(_el).html(_html); |
|
48 | 48 | return _el.children[0].children[0].children[0]; |
|
49 | 49 | }; |
|
50 | 50 | |
|
51 | 51 | function bindDeleteCommentButtons() { |
|
52 | 52 | $('.delete-comment').one('click', function() { |
|
53 | 53 | var comment_id = $(this).data("comment-id"); |
|
54 | 54 | |
|
55 | 55 | if (comment_id){ |
|
56 | 56 | deleteComment(comment_id); |
|
57 | 57 | } |
|
58 | 58 | }); |
|
59 | 59 | } |
|
60 | 60 | |
|
61 | 61 | var deleteComment = function(comment_id) { |
|
62 | 62 | var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id); |
|
63 | 63 | var postData = { |
|
64 | 64 | '_method': 'delete', |
|
65 | 65 | 'csrf_token': CSRF_TOKEN |
|
66 | 66 | }; |
|
67 | 67 | |
|
68 | 68 | var success = function(o) { |
|
69 | 69 | window.location.reload(); |
|
70 | 70 | }; |
|
71 | 71 | ajaxPOST(url, postData, success); |
|
72 | 72 | }; |
|
73 | 73 | |
|
74 | 74 | |
|
75 | 75 | var bindToggleButtons = function() { |
|
76 | 76 | $('.comment-toggle').on('click', function() { |
|
77 | 77 | $(this).parent().nextUntil('tr.line').toggle('inline-comments'); |
|
78 | 78 | }); |
|
79 | 79 | }; |
|
80 | 80 | |
|
81 | 81 | var linkifyComments = function(comments) { |
|
82 | 82 | /* TODO: dan: remove this - it should no longer needed */ |
|
83 | 83 | for (var i = 0; i < comments.length; i++) { |
|
84 | 84 | var comment_id = $(comments[i]).data('comment-id'); |
|
85 | 85 | var prev_comment_id = $(comments[i - 1]).data('comment-id'); |
|
86 | 86 | var next_comment_id = $(comments[i + 1]).data('comment-id'); |
|
87 | 87 | |
|
88 | 88 | // place next/prev links |
|
89 | 89 | if (prev_comment_id) { |
|
90 | 90 | $('#prev_c_' + comment_id).show(); |
|
91 | 91 | $('#prev_c_' + comment_id + " a.arrow_comment_link").attr( |
|
92 | 92 | 'href', '#comment-' + prev_comment_id).removeClass('disabled'); |
|
93 | 93 | } |
|
94 | 94 | if (next_comment_id) { |
|
95 | 95 | $('#next_c_' + comment_id).show(); |
|
96 | 96 | $('#next_c_' + comment_id + " a.arrow_comment_link").attr( |
|
97 | 97 | 'href', '#comment-' + next_comment_id).removeClass('disabled'); |
|
98 | 98 | } |
|
99 | 99 | // place a first link to the total counter |
|
100 | 100 | if (i === 0) { |
|
101 | 101 | $('#inline-comments-counter').attr('href', '#comment-' + comment_id); |
|
102 | 102 | } |
|
103 | 103 | } |
|
104 | 104 | |
|
105 | 105 | }; |
|
106 | 106 | |
|
107 | 107 | |
|
108 | 108 | /* Comment form for main and inline comments */ |
|
109 | 109 | var CommentForm = (function() { |
|
110 | 110 | "use strict"; |
|
111 | 111 | |
|
112 | 112 | function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions) { |
|
113 | 113 | |
|
114 | 114 | this.withLineNo = function(selector) { |
|
115 | 115 | var lineNo = this.lineNo; |
|
116 | 116 | if (lineNo === undefined) { |
|
117 | 117 | return selector |
|
118 | 118 | } else { |
|
119 | 119 | return selector + '_' + lineNo; |
|
120 | 120 | } |
|
121 | 121 | }; |
|
122 | 122 | |
|
123 | 123 | this.commitId = commitId; |
|
124 | 124 | this.pullRequestId = pullRequestId; |
|
125 | 125 | this.lineNo = lineNo; |
|
126 | 126 | this.initAutocompleteActions = initAutocompleteActions; |
|
127 | 127 | |
|
128 | 128 | this.previewButton = this.withLineNo('#preview-btn'); |
|
129 | 129 | this.previewContainer = this.withLineNo('#preview-container'); |
|
130 | 130 | |
|
131 | 131 | this.previewBoxSelector = this.withLineNo('#preview-box'); |
|
132 | 132 | |
|
133 | 133 | this.editButton = this.withLineNo('#edit-btn'); |
|
134 | 134 | this.editContainer = this.withLineNo('#edit-container'); |
|
135 | 135 | |
|
136 | 136 | this.cancelButton = this.withLineNo('#cancel-btn'); |
|
137 | 137 | |
|
138 | 138 | this.statusChange = '#change_status'; |
|
139 | 139 | this.cmBox = this.withLineNo('#text'); |
|
140 | 140 | this.cm = initCommentBoxCodeMirror(this.cmBox, this.initAutocompleteActions); |
|
141 | 141 | |
|
142 | 142 | this.submitForm = formElement; |
|
143 | 143 | this.submitButton = $(this.submitForm).find('input[type="submit"]'); |
|
144 | 144 | this.submitButtonText = this.submitButton.val(); |
|
145 | 145 | |
|
146 | 146 | this.previewUrl = pyroutes.url('changeset_comment_preview', |
|
147 | 147 | {'repo_name': templateContext.repo_name}); |
|
148 | 148 | |
|
149 | 149 | // based on commitId, or pullReuqestId decide where do we submit |
|
150 | 150 | // out data |
|
151 | 151 | if (this.commitId){ |
|
152 | 152 | this.submitUrl = pyroutes.url('changeset_comment', |
|
153 | 153 | {'repo_name': templateContext.repo_name, |
|
154 | 154 | 'revision': this.commitId}); |
|
155 | 155 | |
|
156 | 156 | } else if (this.pullRequestId) { |
|
157 | 157 | this.submitUrl = pyroutes.url('pullrequest_comment', |
|
158 | 158 | {'repo_name': templateContext.repo_name, |
|
159 | 159 | 'pull_request_id': this.pullRequestId}); |
|
160 | 160 | |
|
161 | 161 | } else { |
|
162 | 162 | throw new Error( |
|
163 | 163 | 'CommentForm requires pullRequestId, or commitId to be specified.') |
|
164 | 164 | } |
|
165 | 165 | |
|
166 | 166 | this.getCmInstance = function(){ |
|
167 | 167 | return this.cm |
|
168 | 168 | }; |
|
169 | 169 | |
|
170 | 170 | var self = this; |
|
171 | 171 | |
|
172 | 172 | this.getCommentStatus = function() { |
|
173 | 173 | return $(this.submitForm).find(this.statusChange).val(); |
|
174 | 174 | }; |
|
175 | 175 | |
|
176 | 176 | this.isAllowedToSubmit = function() { |
|
177 | 177 | return !$(this.submitButton).prop('disabled'); |
|
178 | 178 | }; |
|
179 | 179 | |
|
180 | 180 | this.initStatusChangeSelector = function(){ |
|
181 | 181 | var formatChangeStatus = function(state, escapeMarkup) { |
|
182 | 182 | var originalOption = state.element; |
|
183 | 183 | return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' + |
|
184 | 184 | '<span>' + escapeMarkup(state.text) + '</span>'; |
|
185 | 185 | }; |
|
186 | 186 | var formatResult = function(result, container, query, escapeMarkup) { |
|
187 | 187 | return formatChangeStatus(result, escapeMarkup); |
|
188 | 188 | }; |
|
189 | 189 | |
|
190 | 190 | var formatSelection = function(data, container, escapeMarkup) { |
|
191 | 191 | return formatChangeStatus(data, escapeMarkup); |
|
192 | 192 | }; |
|
193 | 193 | |
|
194 | 194 | $(this.submitForm).find(this.statusChange).select2({ |
|
195 | 195 | placeholder: _gettext('Status Review'), |
|
196 | 196 | formatResult: formatResult, |
|
197 | 197 | formatSelection: formatSelection, |
|
198 | 198 | containerCssClass: "drop-menu status_box_menu", |
|
199 | 199 | dropdownCssClass: "drop-menu-dropdown", |
|
200 | 200 | dropdownAutoWidth: true, |
|
201 | 201 | minimumResultsForSearch: -1 |
|
202 | 202 | }); |
|
203 | 203 | $(this.submitForm).find(this.statusChange).on('change', function() { |
|
204 | 204 | var status = self.getCommentStatus(); |
|
205 | 205 | if (status && !self.lineNo) { |
|
206 | 206 | $(self.submitButton).prop('disabled', false); |
|
207 | 207 | } |
|
208 | 208 | //todo, fix this name |
|
209 | 209 | var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status); |
|
210 | 210 | self.cm.setOption('placeholder', placeholderText); |
|
211 | 211 | }) |
|
212 | 212 | }; |
|
213 | 213 | |
|
214 | 214 | // reset the comment form into it's original state |
|
215 | 215 | this.resetCommentFormState = function(content) { |
|
216 | 216 | content = content || ''; |
|
217 | 217 | |
|
218 | 218 | $(this.editContainer).show(); |
|
219 | 219 | $(this.editButton).hide(); |
|
220 | 220 | |
|
221 | 221 | $(this.previewContainer).hide(); |
|
222 | 222 | $(this.previewButton).show(); |
|
223 | 223 | |
|
224 | 224 | this.setActionButtonsDisabled(true); |
|
225 | 225 | self.cm.setValue(content); |
|
226 | 226 | self.cm.setOption("readOnly", false); |
|
227 | 227 | }; |
|
228 | 228 | |
|
229 | 229 | this.submitAjaxPOST = function(url, postData, successHandler, failHandler) { |
|
230 | 230 | failHandler = failHandler || function() {}; |
|
231 | 231 | var postData = toQueryString(postData); |
|
232 | 232 | var request = $.ajax({ |
|
233 | 233 | url: url, |
|
234 | 234 | type: 'POST', |
|
235 | 235 | data: postData, |
|
236 | 236 | headers: {'X-PARTIAL-XHR': true} |
|
237 | 237 | }) |
|
238 | 238 | .done(function(data) { |
|
239 | 239 | successHandler(data); |
|
240 | 240 | }) |
|
241 | 241 | .fail(function(data, textStatus, errorThrown){ |
|
242 | 242 | alert( |
|
243 | 243 | "Error while submitting comment.\n" + |
|
244 | 244 | "Error code {0} ({1}).".format(data.status, data.statusText)); |
|
245 | 245 | failHandler() |
|
246 | 246 | }); |
|
247 | 247 | return request; |
|
248 | 248 | }; |
|
249 | 249 | |
|
250 | 250 | // overwrite a submitHandler, we need to do it for inline comments |
|
251 | 251 | this.setHandleFormSubmit = function(callback) { |
|
252 | 252 | this.handleFormSubmit = callback; |
|
253 | 253 | }; |
|
254 | 254 | |
|
255 | 255 | // default handler for for submit for main comments |
|
256 | 256 | this.handleFormSubmit = function() { |
|
257 | 257 | var text = self.cm.getValue(); |
|
258 | 258 | var status = self.getCommentStatus(); |
|
259 | 259 | |
|
260 | 260 | if (text === "" && !status) { |
|
261 | 261 | return; |
|
262 | 262 | } |
|
263 | 263 | |
|
264 | 264 | var excludeCancelBtn = false; |
|
265 | 265 | var submitEvent = true; |
|
266 | 266 | self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent); |
|
267 | 267 | self.cm.setOption("readOnly", true); |
|
268 | 268 | var postData = { |
|
269 | 269 | 'text': text, |
|
270 | 270 | 'changeset_status': status, |
|
271 | 271 | 'csrf_token': CSRF_TOKEN |
|
272 | 272 | }; |
|
273 | 273 | |
|
274 | 274 | var submitSuccessCallback = function(o) { |
|
275 | 275 | if (status) { |
|
276 | 276 | location.reload(true); |
|
277 | 277 | } else { |
|
278 | 278 | $('#injected_page_comments').append(o.rendered_text); |
|
279 | 279 | self.resetCommentFormState(); |
|
280 | 280 | bindDeleteCommentButtons(); |
|
281 | 281 | timeagoActivate(); |
|
282 | 282 | } |
|
283 | 283 | }; |
|
284 | 284 | var submitFailCallback = function(){ |
|
285 | 285 | self.resetCommentFormState(text) |
|
286 | 286 | }; |
|
287 | 287 | self.submitAjaxPOST( |
|
288 | 288 | self.submitUrl, postData, submitSuccessCallback, submitFailCallback); |
|
289 | 289 | }; |
|
290 | 290 | |
|
291 | 291 | this.previewSuccessCallback = function(o) { |
|
292 | 292 | $(self.previewBoxSelector).html(o); |
|
293 | 293 | $(self.previewBoxSelector).removeClass('unloaded'); |
|
294 | 294 | |
|
295 | 295 | // swap buttons |
|
296 | 296 | $(self.previewButton).hide(); |
|
297 | 297 | $(self.editButton).show(); |
|
298 | 298 | |
|
299 | 299 | // unlock buttons |
|
300 | 300 | self.setActionButtonsDisabled(false); |
|
301 | 301 | }; |
|
302 | 302 | |
|
303 | 303 | this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) { |
|
304 | 304 | excludeCancelBtn = excludeCancelBtn || false; |
|
305 | 305 | submitEvent = submitEvent || false; |
|
306 | 306 | |
|
307 | 307 | $(this.editButton).prop('disabled', state); |
|
308 | 308 | $(this.previewButton).prop('disabled', state); |
|
309 | 309 | |
|
310 | 310 | if (!excludeCancelBtn) { |
|
311 | 311 | $(this.cancelButton).prop('disabled', state); |
|
312 | 312 | } |
|
313 | 313 | |
|
314 | 314 | var submitState = state; |
|
315 | 315 | if (!submitEvent && this.getCommentStatus() && !this.lineNo) { |
|
316 | 316 | // if the value of commit review status is set, we allow |
|
317 | 317 | // submit button, but only on Main form, lineNo means inline |
|
318 | 318 | submitState = false |
|
319 | 319 | } |
|
320 | 320 | $(this.submitButton).prop('disabled', submitState); |
|
321 | 321 | if (submitEvent) { |
|
322 | 322 | $(this.submitButton).val(_gettext('Submitting...')); |
|
323 | 323 | } else { |
|
324 | 324 | $(this.submitButton).val(this.submitButtonText); |
|
325 | 325 | } |
|
326 | 326 | |
|
327 | 327 | }; |
|
328 | 328 | |
|
329 | 329 | // lock preview/edit/submit buttons on load, but exclude cancel button |
|
330 | 330 | var excludeCancelBtn = true; |
|
331 | 331 | this.setActionButtonsDisabled(true, excludeCancelBtn); |
|
332 | 332 | |
|
333 | 333 | // anonymous users don't have access to initialized CM instance |
|
334 | 334 | if (this.cm !== undefined){ |
|
335 | 335 | this.cm.on('change', function(cMirror) { |
|
336 | 336 | if (cMirror.getValue() === "") { |
|
337 | 337 | self.setActionButtonsDisabled(true, excludeCancelBtn) |
|
338 | 338 | } else { |
|
339 | 339 | self.setActionButtonsDisabled(false, excludeCancelBtn) |
|
340 | 340 | } |
|
341 | 341 | }); |
|
342 | 342 | } |
|
343 | 343 | |
|
344 | 344 | $(this.editButton).on('click', function(e) { |
|
345 | 345 | e.preventDefault(); |
|
346 | 346 | |
|
347 | 347 | $(self.previewButton).show(); |
|
348 | 348 | $(self.previewContainer).hide(); |
|
349 | 349 | $(self.editButton).hide(); |
|
350 | 350 | $(self.editContainer).show(); |
|
351 | 351 | |
|
352 | 352 | }); |
|
353 | 353 | |
|
354 | 354 | $(this.previewButton).on('click', function(e) { |
|
355 | 355 | e.preventDefault(); |
|
356 | 356 | var text = self.cm.getValue(); |
|
357 | 357 | |
|
358 | 358 | if (text === "") { |
|
359 | 359 | return; |
|
360 | 360 | } |
|
361 | 361 | |
|
362 | 362 | var postData = { |
|
363 | 363 | 'text': text, |
|
364 | 364 | 'renderer': DEFAULT_RENDERER, |
|
365 | 365 | 'csrf_token': CSRF_TOKEN |
|
366 | 366 | }; |
|
367 | 367 | |
|
368 | 368 | // lock ALL buttons on preview |
|
369 | 369 | self.setActionButtonsDisabled(true); |
|
370 | 370 | |
|
371 | 371 | $(self.previewBoxSelector).addClass('unloaded'); |
|
372 | 372 | $(self.previewBoxSelector).html(_gettext('Loading ...')); |
|
373 | 373 | $(self.editContainer).hide(); |
|
374 | 374 | $(self.previewContainer).show(); |
|
375 | 375 | |
|
376 | 376 | // by default we reset state of comment preserving the text |
|
377 | 377 | var previewFailCallback = function(){ |
|
378 | 378 | self.resetCommentFormState(text) |
|
379 | 379 | }; |
|
380 | 380 | self.submitAjaxPOST( |
|
381 | 381 | self.previewUrl, postData, self.previewSuccessCallback, previewFailCallback); |
|
382 | 382 | |
|
383 | 383 | }); |
|
384 | 384 | |
|
385 | 385 | $(this.submitForm).submit(function(e) { |
|
386 | 386 | e.preventDefault(); |
|
387 | 387 | var allowedToSubmit = self.isAllowedToSubmit(); |
|
388 | 388 | if (!allowedToSubmit){ |
|
389 | 389 | return false; |
|
390 | 390 | } |
|
391 | 391 | self.handleFormSubmit(); |
|
392 | 392 | }); |
|
393 | 393 | |
|
394 | 394 | } |
|
395 | 395 | |
|
396 | 396 | return CommentForm; |
|
397 | 397 | })(); |
|
398 | 398 | |
|
399 | 399 | var CommentsController = function() { /* comments controller */ |
|
400 | 400 | var self = this; |
|
401 | 401 | |
|
402 | 402 | this.cancelComment = function(node) { |
|
403 | 403 | var $node = $(node); |
|
404 | 404 | var $td = $node.closest('td'); |
|
405 | 405 | $node.closest('.comment-inline-form').removeClass('comment-inline-form-open'); |
|
406 | 406 | return false; |
|
407 | 407 | }; |
|
408 | 408 | |
|
409 | 409 | this.getLineNumber = function(node) { |
|
410 | 410 | var $node = $(node); |
|
411 | 411 | return $node.closest('td').attr('data-line-number'); |
|
412 | 412 | }; |
|
413 | 413 | |
|
414 | this.scrollToComment = function(node, offset) { | |
|
414 | this.scrollToComment = function(node, offset, outdated) { | |
|
415 | var outdated = outdated || false; | |
|
416 | var klass = outdated ? 'div.comment-outdated' : 'div.comment-current'; | |
|
417 | ||
|
415 | 418 | if (!node) { |
|
416 | 419 | node = $('.comment-selected'); |
|
417 | 420 | if (!node.length) { |
|
418 | 421 | node = $('comment-current') |
|
419 | 422 | } |
|
420 | 423 | } |
|
421 |
$comment = $(node).closest( |
|
|
422 |
$comments = $( |
|
|
424 | $comment = $(node).closest(klass); | |
|
425 | $comments = $(klass); | |
|
423 | 426 | |
|
424 | 427 | $('.comment-selected').removeClass('comment-selected'); |
|
425 | 428 | |
|
426 |
var nextIdx = $( |
|
|
429 | var nextIdx = $(klass).index($comment) + offset; | |
|
427 | 430 | if (nextIdx >= $comments.length) { |
|
428 | 431 | nextIdx = 0; |
|
429 | 432 | } |
|
430 |
var $next = $( |
|
|
433 | var $next = $(klass).eq(nextIdx); | |
|
431 | 434 | var $cb = $next.closest('.cb'); |
|
432 | 435 | $cb.removeClass('cb-collapsed'); |
|
433 | 436 | |
|
434 | 437 | var $filediffCollapseState = $cb.closest('.filediff').prev(); |
|
435 | 438 | $filediffCollapseState.prop('checked', false); |
|
436 | 439 | $next.addClass('comment-selected'); |
|
437 | 440 | scrollToElement($next); |
|
438 | 441 | return false; |
|
439 | 442 | }; |
|
440 | 443 | |
|
441 | 444 | this.nextComment = function(node) { |
|
442 | 445 | return self.scrollToComment(node, 1); |
|
443 | 446 | }; |
|
444 | 447 | |
|
445 | 448 | this.prevComment = function(node) { |
|
446 | 449 | return self.scrollToComment(node, -1); |
|
447 | 450 | }; |
|
448 | 451 | |
|
452 | this.nextOutdatedComment = function(node) { | |
|
453 | return self.scrollToComment(node, 1, true); | |
|
454 | }; | |
|
455 | ||
|
456 | this.prevOutdatedComment = function(node) { | |
|
457 | return self.scrollToComment(node, -1, true); | |
|
458 | }; | |
|
459 | ||
|
449 | 460 | this.deleteComment = function(node) { |
|
450 | 461 | if (!confirm(_gettext('Delete this comment?'))) { |
|
451 | 462 | return false; |
|
452 | 463 | } |
|
453 | 464 | var $node = $(node); |
|
454 | 465 | var $td = $node.closest('td'); |
|
455 | 466 | var $comment = $node.closest('.comment'); |
|
456 | 467 | var comment_id = $comment.attr('data-comment-id'); |
|
457 | 468 | var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id); |
|
458 | 469 | var postData = { |
|
459 | 470 | '_method': 'delete', |
|
460 | 471 | 'csrf_token': CSRF_TOKEN |
|
461 | 472 | }; |
|
462 | 473 | |
|
463 | 474 | $comment.addClass('comment-deleting'); |
|
464 | 475 | $comment.hide('fast'); |
|
465 | 476 | |
|
466 | 477 | var success = function(response) { |
|
467 | 478 | $comment.remove(); |
|
468 | 479 | return false; |
|
469 | 480 | }; |
|
470 | 481 | var failure = function(data, textStatus, xhr) { |
|
471 | 482 | alert("error processing request: " + textStatus); |
|
472 | 483 | $comment.show('fast'); |
|
473 | 484 | $comment.removeClass('comment-deleting'); |
|
474 | 485 | return false; |
|
475 | 486 | }; |
|
476 | 487 | ajaxPOST(url, postData, success, failure); |
|
477 | 488 | }; |
|
478 | 489 | |
|
479 | 490 | this.toggleWideMode = function (node) { |
|
480 | 491 | if ($('#content').hasClass('wrapper')) { |
|
481 | 492 | $('#content').removeClass("wrapper"); |
|
482 | 493 | $('#content').addClass("wide-mode-wrapper"); |
|
483 | 494 | $(node).addClass('btn-success'); |
|
484 | 495 | } else { |
|
485 | 496 | $('#content').removeClass("wide-mode-wrapper"); |
|
486 | 497 | $('#content').addClass("wrapper"); |
|
487 | 498 | $(node).removeClass('btn-success'); |
|
488 | 499 | } |
|
489 | 500 | return false; |
|
490 | 501 | }; |
|
491 | 502 | |
|
492 | 503 | this.toggleComments = function(node, show) { |
|
493 | 504 | var $filediff = $(node).closest('.filediff'); |
|
494 | 505 | if (show === true) { |
|
495 | 506 | $filediff.removeClass('hide-comments'); |
|
496 | 507 | } else if (show === false) { |
|
497 | 508 | $filediff.find('.hide-line-comments').removeClass('hide-line-comments'); |
|
498 | 509 | $filediff.addClass('hide-comments'); |
|
499 | 510 | } else { |
|
500 | 511 | $filediff.find('.hide-line-comments').removeClass('hide-line-comments'); |
|
501 | 512 | $filediff.toggleClass('hide-comments'); |
|
502 | 513 | } |
|
503 | 514 | return false; |
|
504 | 515 | }; |
|
505 | 516 | |
|
506 | 517 | this.toggleLineComments = function(node) { |
|
507 | 518 | self.toggleComments(node, true); |
|
508 | 519 | var $node = $(node); |
|
509 | 520 | $node.closest('tr').toggleClass('hide-line-comments'); |
|
510 | 521 | }; |
|
511 | 522 | |
|
512 | 523 | this.createComment = function(node) { |
|
513 | 524 | var $node = $(node); |
|
514 | 525 | var $td = $node.closest('td'); |
|
515 | 526 | var $form = $td.find('.comment-inline-form'); |
|
516 | 527 | |
|
517 | 528 | if (!$form.length) { |
|
518 | 529 | var tmpl = $('#cb-comment-inline-form-template').html(); |
|
519 | 530 | var $filediff = $node.closest('.filediff'); |
|
520 | 531 | $filediff.removeClass('hide-comments'); |
|
521 | 532 | var f_path = $filediff.attr('data-f-path'); |
|
522 | 533 | var lineno = self.getLineNumber(node); |
|
523 | 534 | tmpl = tmpl.format(f_path, lineno); |
|
524 | 535 | $form = $(tmpl); |
|
525 | 536 | |
|
526 | 537 | var $comments = $td.find('.inline-comments'); |
|
527 | 538 | if (!$comments.length) { |
|
528 | 539 | $comments = $( |
|
529 | 540 | $('#cb-comments-inline-container-template').html()); |
|
530 | 541 | $td.append($comments); |
|
531 | 542 | } |
|
532 | 543 | |
|
533 | 544 | $td.find('.cb-comment-add-button').before($form); |
|
534 | 545 | |
|
535 | 546 | var pullRequestId = templateContext.pull_request_data.pull_request_id; |
|
536 | 547 | var commitId = templateContext.commit_data.commit_id; |
|
537 | 548 | var _form = $form[0]; |
|
538 | 549 | var commentForm = new CommentForm(_form, commitId, pullRequestId, lineno, false); |
|
539 | 550 | var cm = commentForm.getCmInstance(); |
|
540 | 551 | |
|
541 | 552 | // set a CUSTOM submit handler for inline comments. |
|
542 | 553 | commentForm.setHandleFormSubmit(function(o) { |
|
543 | 554 | var text = commentForm.cm.getValue(); |
|
544 | 555 | |
|
545 | 556 | if (text === "") { |
|
546 | 557 | return; |
|
547 | 558 | } |
|
548 | 559 | |
|
549 | 560 | if (lineno === undefined) { |
|
550 | 561 | alert('missing line !'); |
|
551 | 562 | return; |
|
552 | 563 | } |
|
553 | 564 | if (f_path === undefined) { |
|
554 | 565 | alert('missing file path !'); |
|
555 | 566 | return; |
|
556 | 567 | } |
|
557 | 568 | |
|
558 | 569 | var excludeCancelBtn = false; |
|
559 | 570 | var submitEvent = true; |
|
560 | 571 | commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent); |
|
561 | 572 | commentForm.cm.setOption("readOnly", true); |
|
562 | 573 | var postData = { |
|
563 | 574 | 'text': text, |
|
564 | 575 | 'f_path': f_path, |
|
565 | 576 | 'line': lineno, |
|
566 | 577 | 'csrf_token': CSRF_TOKEN |
|
567 | 578 | }; |
|
568 | 579 | var submitSuccessCallback = function(json_data) { |
|
569 | 580 | $form.remove(); |
|
570 | 581 | try { |
|
571 | 582 | var html = json_data.rendered_text; |
|
572 | 583 | var lineno = json_data.line_no; |
|
573 | 584 | var target_id = json_data.target_id; |
|
574 | 585 | |
|
575 | 586 | $comments.find('.cb-comment-add-button').before(html); |
|
576 | 587 | |
|
577 | 588 | } catch (e) { |
|
578 | 589 | console.error(e); |
|
579 | 590 | } |
|
580 | 591 | |
|
581 | 592 | // re trigger the linkification of next/prev navigation |
|
582 | 593 | linkifyComments($('.inline-comment-injected')); |
|
583 | 594 | timeagoActivate(); |
|
584 | 595 | bindDeleteCommentButtons(); |
|
585 | 596 | commentForm.setActionButtonsDisabled(false); |
|
586 | 597 | |
|
587 | 598 | }; |
|
588 | 599 | var submitFailCallback = function(){ |
|
589 | 600 | commentForm.resetCommentFormState(text) |
|
590 | 601 | }; |
|
591 | 602 | commentForm.submitAjaxPOST( |
|
592 | 603 | commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback); |
|
593 | 604 | }); |
|
594 | 605 | |
|
595 | 606 | setTimeout(function() { |
|
596 | 607 | // callbacks |
|
597 | 608 | if (cm !== undefined) { |
|
598 | 609 | cm.focus(); |
|
599 | 610 | } |
|
600 | 611 | }, 10); |
|
601 | 612 | |
|
602 | 613 | $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({ |
|
603 | 614 | form: _form, |
|
604 | 615 | parent: $td[0], |
|
605 | 616 | lineno: lineno, |
|
606 | 617 | f_path: f_path} |
|
607 | 618 | ); |
|
608 | 619 | } |
|
609 | 620 | |
|
610 | 621 | $form.addClass('comment-inline-form-open'); |
|
611 | 622 | }; |
|
612 | 623 | |
|
613 | 624 | this.renderInlineComments = function(file_comments) { |
|
614 | 625 | show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true; |
|
615 | 626 | |
|
616 | 627 | for (var i = 0; i < file_comments.length; i++) { |
|
617 | 628 | var box = file_comments[i]; |
|
618 | 629 | |
|
619 | 630 | var target_id = $(box).attr('target_id'); |
|
620 | 631 | |
|
621 | 632 | // actually comments with line numbers |
|
622 | 633 | var comments = box.children; |
|
623 | 634 | |
|
624 | 635 | for (var j = 0; j < comments.length; j++) { |
|
625 | 636 | var data = { |
|
626 | 637 | 'rendered_text': comments[j].outerHTML, |
|
627 | 638 | 'line_no': $(comments[j]).attr('line'), |
|
628 | 639 | 'target_id': target_id |
|
629 | 640 | }; |
|
630 | 641 | } |
|
631 | 642 | } |
|
632 | 643 | |
|
633 | 644 | // since order of injection is random, we're now re-iterating |
|
634 | 645 | // from correct order and filling in links |
|
635 | 646 | linkifyComments($('.inline-comment-injected')); |
|
636 | 647 | bindDeleteCommentButtons(); |
|
637 | 648 | firefoxAnchorFix(); |
|
638 | 649 | }; |
|
639 | 650 | |
|
640 | 651 | }; |
@@ -1,418 +1,418 b'' | |||
|
1 | 1 | ## -*- coding: utf-8 -*- |
|
2 | 2 | |
|
3 | 3 | <%inherit file="/base/base.html"/> |
|
4 | 4 | |
|
5 | 5 | <%def name="title()"> |
|
6 | 6 | ${_('%s Changelog') % c.repo_name} |
|
7 | 7 | %if c.changelog_for_path: |
|
8 | 8 | /${c.changelog_for_path} |
|
9 | 9 | %endif |
|
10 | 10 | %if c.rhodecode_name: |
|
11 | 11 | · ${h.branding(c.rhodecode_name)} |
|
12 | 12 | %endif |
|
13 | 13 | </%def> |
|
14 | 14 | |
|
15 | 15 | <%def name="breadcrumbs_links()"> |
|
16 | 16 | %if c.changelog_for_path: |
|
17 | 17 | /${c.changelog_for_path} |
|
18 | 18 | %endif |
|
19 | 19 | ${ungettext('showing %d out of %d commit', 'showing %d out of %d commits', c.showing_commits) % (c.showing_commits, c.total_cs)} |
|
20 | 20 | </%def> |
|
21 | 21 | |
|
22 | 22 | <%def name="menu_bar_nav()"> |
|
23 | 23 | ${self.menu_items(active='repositories')} |
|
24 | 24 | </%def> |
|
25 | 25 | |
|
26 | 26 | <%def name="menu_bar_subnav()"> |
|
27 | 27 | ${self.repo_menu(active='changelog')} |
|
28 | 28 | </%def> |
|
29 | 29 | |
|
30 | 30 | <%def name="main()"> |
|
31 | 31 | |
|
32 | 32 | <div class="box"> |
|
33 | 33 | <div class="title"> |
|
34 | 34 | ${self.repo_page_title(c.rhodecode_db_repo)} |
|
35 | 35 | <ul class="links"> |
|
36 | 36 | <li> |
|
37 | 37 | <a href="#" class="btn btn-small" id="rev_range_container" style="display:none;"></a> |
|
38 | 38 | %if c.rhodecode_db_repo.fork: |
|
39 | 39 | <span> |
|
40 | 40 | <a id="compare_fork_button" |
|
41 | 41 | title="${_('Compare fork with %s' % c.rhodecode_db_repo.fork.repo_name)}" |
|
42 | 42 | class="btn btn-small" |
|
43 | 43 | href="${h.url('compare_url', |
|
44 | 44 | repo_name=c.rhodecode_db_repo.fork.repo_name, |
|
45 | 45 | source_ref_type=c.rhodecode_db_repo.landing_rev[0], |
|
46 | 46 | source_ref=c.rhodecode_db_repo.landing_rev[1], |
|
47 | 47 | target_repo=c.repo_name, |
|
48 | 48 | target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0], |
|
49 | 49 | target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], |
|
50 | 50 | merge=1) |
|
51 | 51 | }"> |
|
52 | 52 | <i class="icon-loop"></i> |
|
53 | 53 | ${_('Compare fork with Parent (%s)' % c.rhodecode_db_repo.fork.repo_name)} |
|
54 | 54 | </a> |
|
55 | 55 | </span> |
|
56 | 56 | %endif |
|
57 | 57 | |
|
58 | 58 | ## pr open link |
|
59 | 59 | %if h.is_hg(c.rhodecode_repo) or h.is_git(c.rhodecode_repo): |
|
60 | 60 | <span> |
|
61 | 61 | <a id="open_new_pull_request" class="btn btn-small btn-success" href="${h.url('pullrequest_home',repo_name=c.repo_name)}"> |
|
62 | 62 | ${_('Open new pull request')} |
|
63 | 63 | </a> |
|
64 | 64 | </span> |
|
65 | 65 | %endif |
|
66 | 66 | |
|
67 | 67 | ## clear selection |
|
68 | 68 | <div title="${_('Clear selection')}" class="btn" id="rev_range_clear" style="display:none"> |
|
69 | 69 | ${_('Clear selection')} |
|
70 | 70 | </div> |
|
71 | 71 | |
|
72 | 72 | </li> |
|
73 | 73 | </ul> |
|
74 | 74 | </div> |
|
75 | 75 | |
|
76 | 76 | % if c.pagination: |
|
77 | 77 | |
|
78 | 78 | <div class="graph-header"> |
|
79 | 79 | <div id="filter_changelog"> |
|
80 | 80 | ${h.hidden('branch_filter')} |
|
81 | 81 | %if c.selected_name: |
|
82 | 82 | <div class="btn btn-default" id="clear_filter" > |
|
83 | 83 | ${_('Clear filter')} |
|
84 | 84 | </div> |
|
85 | 85 | %endif |
|
86 | 86 | </div> |
|
87 | 87 | ${self.breadcrumbs('breadcrumbs_light')} |
|
88 | 88 | </div> |
|
89 | 89 | |
|
90 | 90 | <div id="graph"> |
|
91 | 91 | <div class="graph-col-wrapper"> |
|
92 | 92 | <div id="graph_nodes"> |
|
93 | 93 | <div id="graph_canvas" data-graph='${c.jsdata|n}'></div> |
|
94 | 94 | </div> |
|
95 | 95 | <div id="graph_content" class="main-content graph_full_width"> |
|
96 | 96 | |
|
97 | 97 | <div class="table"> |
|
98 | 98 | <table id="changesets" class="rctable"> |
|
99 | 99 | <tr> |
|
100 | 100 | ## checkbox |
|
101 | 101 | <th></th> |
|
102 | 102 | <th colspan="2"></th> |
|
103 | 103 | |
|
104 | 104 | <th>${_('Commit')}</th> |
|
105 | 105 | ## commit message expand arrow |
|
106 | 106 | <th></th> |
|
107 | 107 | <th>${_('Commit Message')}</th> |
|
108 | 108 | |
|
109 | 109 | <th>${_('Age')}</th> |
|
110 | 110 | <th>${_('Author')}</th> |
|
111 | 111 | |
|
112 | 112 | <th>${_('Refs')}</th> |
|
113 | 113 | </tr> |
|
114 | 114 | <tbody> |
|
115 | 115 | %for cnt,commit in enumerate(c.pagination): |
|
116 | 116 | <tr id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}"> |
|
117 | 117 | |
|
118 | 118 | <td class="td-checkbox"> |
|
119 | 119 | ${h.checkbox(commit.raw_id,class_="commit-range")} |
|
120 | 120 | </td> |
|
121 | 121 | <td class="td-status"> |
|
122 | 122 | |
|
123 | 123 | %if c.statuses.get(commit.raw_id): |
|
124 | 124 | <div class="changeset-status-ico"> |
|
125 | 125 | %if c.statuses.get(commit.raw_id)[2]: |
|
126 | 126 | <a class="tooltip" title="${_('Commit status: %s\nClick to open associated pull request #%s') % (h.commit_status_lbl(c.statuses.get(commit.raw_id)[0]), c.statuses.get(commit.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(commit.raw_id)[3],pull_request_id=c.statuses.get(commit.raw_id)[2])}"> |
|
127 | 127 | <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div> |
|
128 | 128 | </a> |
|
129 | 129 | %else: |
|
130 | 130 | <a class="tooltip" title="${_('Commit status: %s') % h.commit_status_lbl(c.statuses.get(commit.raw_id)[0])}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}"> |
|
131 | 131 | <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div> |
|
132 | 132 | </a> |
|
133 | 133 | %endif |
|
134 | 134 | </div> |
|
135 | 135 | %else: |
|
136 | 136 | <div class="tooltip flag_status not_reviewed" title="${_('Commit status: Not Reviewed')}"></div> |
|
137 | 137 | %endif |
|
138 | 138 | </td> |
|
139 | 139 | <td class="td-comments comments-col"> |
|
140 | 140 | %if c.comments.get(commit.raw_id): |
|
141 | 141 | <a title="${_('Commit has comments')}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}"> |
|
142 |
<i class="icon-comment |
|
|
142 | <i class="icon-comment"></i> ${len(c.comments[commit.raw_id])} | |
|
143 | 143 | </a> |
|
144 | 144 | %endif |
|
145 | 145 | </td> |
|
146 | 146 | <td class="td-hash"> |
|
147 | 147 | <code> |
|
148 | 148 | <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id)}"> |
|
149 | 149 | <span class="commit_hash">${h.show_id(commit)}</span> |
|
150 | 150 | </a> |
|
151 | 151 | </code> |
|
152 | 152 | </td> |
|
153 | 153 | <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_('Expand commit message')}"> |
|
154 | 154 | <div class="show_more_col"> |
|
155 | 155 | <i class="show_more"></i> |
|
156 | 156 | </div> |
|
157 | 157 | </td> |
|
158 | 158 | <td class="td-description mid"> |
|
159 | 159 | <div class="log-container truncate-wrap"> |
|
160 | 160 | <div class="message truncate" id="c-${commit.raw_id}">${h.urlify_commit_message(commit.message, c.repo_name)}</div> |
|
161 | 161 | </div> |
|
162 | 162 | </td> |
|
163 | 163 | |
|
164 | 164 | <td class="td-time"> |
|
165 | 165 | ${h.age_component(commit.date)} |
|
166 | 166 | </td> |
|
167 | 167 | <td class="td-user"> |
|
168 | 168 | ${self.gravatar_with_user(commit.author)} |
|
169 | 169 | </td> |
|
170 | 170 | |
|
171 | 171 | <td class="td-tags tags-col"> |
|
172 | 172 | <div id="t-${commit.raw_id}"> |
|
173 | 173 | ## branch |
|
174 | 174 | %if commit.branch: |
|
175 | 175 | <span class="branchtag tag" title="${_('Branch %s') % commit.branch}"> |
|
176 | 176 | <a href="${h.url('changelog_home',repo_name=c.repo_name,branch=commit.branch)}"><i class="icon-code-fork"></i>${h.shorter(commit.branch)}</a> |
|
177 | 177 | </span> |
|
178 | 178 | %endif |
|
179 | 179 | |
|
180 | 180 | ## bookmarks |
|
181 | 181 | %if h.is_hg(c.rhodecode_repo): |
|
182 | 182 | %for book in commit.bookmarks: |
|
183 | 183 | <span class="tag booktag" title="${_('Bookmark %s') % book}"> |
|
184 | 184 | <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-bookmark"></i>${h.shorter(book)}</a> |
|
185 | 185 | </span> |
|
186 | 186 | %endfor |
|
187 | 187 | %endif |
|
188 | 188 | |
|
189 | 189 | ## tags |
|
190 | 190 | %for tag in commit.tags: |
|
191 | 191 | <span class="tagtag tag" title="${_('Tag %s') % tag}"> |
|
192 | 192 | <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-tag"></i>${h.shorter(tag)}</a> |
|
193 | 193 | </span> |
|
194 | 194 | %endfor |
|
195 | 195 | |
|
196 | 196 | </div> |
|
197 | 197 | </td> |
|
198 | 198 | </tr> |
|
199 | 199 | %endfor |
|
200 | 200 | </tbody> |
|
201 | 201 | </table> |
|
202 | 202 | </div> |
|
203 | 203 | </div> |
|
204 | 204 | </div> |
|
205 | 205 | <div class="pagination-wh pagination-left"> |
|
206 | 206 | ${c.pagination.pager('$link_previous ~2~ $link_next')} |
|
207 | 207 | </div> |
|
208 | 208 | |
|
209 | 209 | <script type="text/javascript" src="${h.asset('js/jquery.commits-graph.js')}"></script> |
|
210 | 210 | <script type="text/javascript"> |
|
211 | 211 | var cache = {}; |
|
212 | 212 | $(function(){ |
|
213 | 213 | |
|
214 | 214 | // Create links to commit ranges when range checkboxes are selected |
|
215 | 215 | var $commitCheckboxes = $('.commit-range'); |
|
216 | 216 | // cache elements |
|
217 | 217 | var $commitRangeContainer = $('#rev_range_container'); |
|
218 | 218 | var $commitRangeClear = $('#rev_range_clear'); |
|
219 | 219 | |
|
220 | 220 | var checkboxRangeSelector = function(e){ |
|
221 | 221 | var selectedCheckboxes = []; |
|
222 | 222 | for (pos in $commitCheckboxes){ |
|
223 | 223 | if($commitCheckboxes[pos].checked){ |
|
224 | 224 | selectedCheckboxes.push($commitCheckboxes[pos]); |
|
225 | 225 | } |
|
226 | 226 | } |
|
227 | 227 | var open_new_pull_request = $('#open_new_pull_request'); |
|
228 | 228 | if(open_new_pull_request){ |
|
229 | 229 | var selected_changes = selectedCheckboxes.length; |
|
230 | 230 | if (selected_changes > 1 || selected_changes == 1 && templateContext.repo_type != 'hg') { |
|
231 | 231 | open_new_pull_request.hide(); |
|
232 | 232 | } else { |
|
233 | 233 | if (selected_changes == 1) { |
|
234 | 234 | open_new_pull_request.html(_gettext('Open new pull request for selected commit')); |
|
235 | 235 | } else if (selected_changes == 0) { |
|
236 | 236 | open_new_pull_request.html(_gettext('Open new pull request')); |
|
237 | 237 | } |
|
238 | 238 | open_new_pull_request.show(); |
|
239 | 239 | } |
|
240 | 240 | } |
|
241 | 241 | |
|
242 | 242 | if (selectedCheckboxes.length>0){ |
|
243 | 243 | var revEnd = selectedCheckboxes[0].name; |
|
244 | 244 | var revStart = selectedCheckboxes[selectedCheckboxes.length-1].name; |
|
245 | 245 | var url = pyroutes.url('changeset_home', |
|
246 | 246 | {'repo_name': '${c.repo_name}', |
|
247 | 247 | 'revision': revStart+'...'+revEnd}); |
|
248 | 248 | |
|
249 | 249 | var link = (revStart == revEnd) |
|
250 | 250 | ? _gettext('Show selected commit __S') |
|
251 | 251 | : _gettext('Show selected commits __S ... __E'); |
|
252 | 252 | |
|
253 | 253 | link = link.replace('__S', revStart.substr(0,6)); |
|
254 | 254 | link = link.replace('__E', revEnd.substr(0,6)); |
|
255 | 255 | |
|
256 | 256 | $commitRangeContainer |
|
257 | 257 | .attr('href',url) |
|
258 | 258 | .html(link) |
|
259 | 259 | .show(); |
|
260 | 260 | |
|
261 | 261 | $commitRangeClear.show(); |
|
262 | 262 | var _url = pyroutes.url('pullrequest_home', |
|
263 | 263 | {'repo_name': '${c.repo_name}', |
|
264 | 264 | 'commit': revEnd}); |
|
265 | 265 | open_new_pull_request.attr('href', _url); |
|
266 | 266 | $('#compare_fork_button').hide(); |
|
267 | 267 | } else { |
|
268 | 268 | $commitRangeContainer.hide(); |
|
269 | 269 | $commitRangeClear.hide(); |
|
270 | 270 | |
|
271 | 271 | %if c.branch_name: |
|
272 | 272 | var _url = pyroutes.url('pullrequest_home', |
|
273 | 273 | {'repo_name': '${c.repo_name}', |
|
274 | 274 | 'branch':'${c.branch_name}'}); |
|
275 | 275 | open_new_pull_request.attr('href', _url); |
|
276 | 276 | %else: |
|
277 | 277 | var _url = pyroutes.url('pullrequest_home', |
|
278 | 278 | {'repo_name': '${c.repo_name}'}); |
|
279 | 279 | open_new_pull_request.attr('href', _url); |
|
280 | 280 | %endif |
|
281 | 281 | $('#compare_fork_button').show(); |
|
282 | 282 | } |
|
283 | 283 | }; |
|
284 | 284 | |
|
285 | 285 | $commitCheckboxes.on('click', checkboxRangeSelector); |
|
286 | 286 | |
|
287 | 287 | $commitRangeClear.on('click',function(e) { |
|
288 | 288 | $commitCheckboxes.attr('checked', false) |
|
289 | 289 | checkboxRangeSelector(); |
|
290 | 290 | e.preventDefault(); |
|
291 | 291 | }); |
|
292 | 292 | |
|
293 | 293 | // make sure the buttons are consistent when navigate back and forth |
|
294 | 294 | checkboxRangeSelector(); |
|
295 | 295 | |
|
296 | 296 | |
|
297 | 297 | var msgs = $('.message'); |
|
298 | 298 | // get first element height |
|
299 | 299 | var el = $('#graph_content .container')[0]; |
|
300 | 300 | var row_h = el.clientHeight; |
|
301 | 301 | for (var i=0; i < msgs.length; i++) { |
|
302 | 302 | var m = msgs[i]; |
|
303 | 303 | |
|
304 | 304 | var h = m.clientHeight; |
|
305 | 305 | var pad = $(m).css('padding'); |
|
306 | 306 | if (h > row_h) { |
|
307 | 307 | var offset = row_h - (h+12); |
|
308 | 308 | $(m.nextElementSibling).css('display','block'); |
|
309 | 309 | $(m.nextElementSibling).css('margin-top',offset+'px'); |
|
310 | 310 | } |
|
311 | 311 | } |
|
312 | 312 | |
|
313 | 313 | $('.expand_commit').on('click',function(e){ |
|
314 | 314 | var target_expand = $(this); |
|
315 | 315 | var cid = target_expand.data('commitId'); |
|
316 | 316 | |
|
317 | 317 | if (target_expand.hasClass('open')){ |
|
318 | 318 | $('#c-'+cid).css({'height': '1.5em', 'white-space': 'nowrap', 'text-overflow': 'ellipsis', 'overflow':'hidden'}); |
|
319 | 319 | $('#t-'+cid).css({'height': 'auto', 'line-height': '.9em', 'text-overflow': 'ellipsis', 'overflow':'hidden', 'white-space':'nowrap'}); |
|
320 | 320 | target_expand.removeClass('open'); |
|
321 | 321 | } |
|
322 | 322 | else { |
|
323 | 323 | $('#c-'+cid).css({'height': 'auto', 'white-space': 'pre-line', 'text-overflow': 'initial', 'overflow':'visible'}); |
|
324 | 324 | $('#t-'+cid).css({'height': 'auto', 'max-height': 'none', 'text-overflow': 'initial', 'overflow':'visible', 'white-space':'normal'}); |
|
325 | 325 | target_expand.addClass('open'); |
|
326 | 326 | } |
|
327 | 327 | // redraw the graph |
|
328 | 328 | graph_options.height = $("#changesets").height(); |
|
329 | 329 | $("canvas").remove(); |
|
330 | 330 | $("[data-graph]").commits(graph_options); |
|
331 | 331 | }); |
|
332 | 332 | |
|
333 | 333 | $("#clear_filter").on("click", function() { |
|
334 | 334 | var filter = {'repo_name': '${c.repo_name}'}; |
|
335 | 335 | window.location = pyroutes.url('changelog_home', filter); |
|
336 | 336 | }); |
|
337 | 337 | |
|
338 | 338 | $("#branch_filter").select2({ |
|
339 | 339 | 'dropdownAutoWidth': true, |
|
340 | 340 | 'width': 'resolve', |
|
341 | 341 | 'placeholder': "${c.selected_name or _('Filter changelog')}", |
|
342 | 342 | containerCssClass: "drop-menu", |
|
343 | 343 | dropdownCssClass: "drop-menu-dropdown", |
|
344 | 344 | query: function(query){ |
|
345 | 345 | var key = 'cache'; |
|
346 | 346 | var cached = cache[key] ; |
|
347 | 347 | if(cached) { |
|
348 | 348 | var data = {results: []}; |
|
349 | 349 | //filter results |
|
350 | 350 | $.each(cached.results, function(){ |
|
351 | 351 | var section = this.text; |
|
352 | 352 | var children = []; |
|
353 | 353 | $.each(this.children, function(){ |
|
354 | 354 | if(query.term.length == 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ){ |
|
355 | 355 | children.push({'id': this.id, 'text': this.text, 'type': this.type}) |
|
356 | 356 | } |
|
357 | 357 | }); |
|
358 | 358 | data.results.push({'text': section, 'children': children}); |
|
359 | 359 | query.callback({results: data.results}); |
|
360 | 360 | }); |
|
361 | 361 | }else{ |
|
362 | 362 | $.ajax({ |
|
363 | 363 | url: pyroutes.url('repo_refs_changelog_data', {'repo_name': '${c.repo_name}'}), |
|
364 | 364 | data: {}, |
|
365 | 365 | dataType: 'json', |
|
366 | 366 | type: 'GET', |
|
367 | 367 | success: function(data) { |
|
368 | 368 | cache[key] = data; |
|
369 | 369 | query.callback({results: data.results}); |
|
370 | 370 | } |
|
371 | 371 | }) |
|
372 | 372 | } |
|
373 | 373 | } |
|
374 | 374 | }); |
|
375 | 375 | |
|
376 | 376 | $('#branch_filter').on('change', function(e){ |
|
377 | 377 | var data = $('#branch_filter').select2('data'); |
|
378 | 378 | var selected = data.text; |
|
379 | 379 | var filter = {'repo_name': '${c.repo_name}'}; |
|
380 | 380 | if(data.type == 'branch' || data.type == 'branch_closed'){ |
|
381 | 381 | filter["branch"] = selected; |
|
382 | 382 | } |
|
383 | 383 | else if (data.type == 'book'){ |
|
384 | 384 | filter["bookmark"] = selected; |
|
385 | 385 | } |
|
386 | 386 | window.location = pyroutes.url('changelog_home', filter); |
|
387 | 387 | }); |
|
388 | 388 | |
|
389 | 389 | // Determine max number of edges per row in graph |
|
390 | 390 | var jsdata = $.parseJSON($("[data-graph]").attr('data-graph')); |
|
391 | 391 | var edgeCount = 1; |
|
392 | 392 | $.each(jsdata, function(i, item){ |
|
393 | 393 | $.each(item[2], function(key, value) { |
|
394 | 394 | if (value[1] > edgeCount){ |
|
395 | 395 | edgeCount = value[1]; |
|
396 | 396 | } |
|
397 | 397 | }); |
|
398 | 398 | }); |
|
399 | 399 | var x_step = Math.min(18, Math.floor(86 / edgeCount)); |
|
400 | 400 | var graph_options = { |
|
401 | 401 | width: 100, |
|
402 | 402 | height: $("#changesets").height(), |
|
403 | 403 | x_step: x_step, |
|
404 | 404 | y_step: 42, |
|
405 | 405 | dotRadius: 3.5, |
|
406 | 406 | lineWidth: 2.5 |
|
407 | 407 | }; |
|
408 | 408 | $("[data-graph]").commits(graph_options); |
|
409 | 409 | |
|
410 | 410 | }); |
|
411 | 411 | |
|
412 | 412 | </script> |
|
413 | 413 | %else: |
|
414 | 414 | ${_('There are no changes yet')} |
|
415 | 415 | %endif |
|
416 | 416 | </div> |
|
417 | 417 | </div> |
|
418 | 418 | </%def> |
@@ -1,136 +1,136 b'' | |||
|
1 | 1 | ## -*- coding: utf-8 -*- |
|
2 | 2 | <%namespace name="base" file="/base/base.html"/> |
|
3 | 3 | %if c.repo_commits: |
|
4 | 4 | <table class="rctable repo_summary table_disp"> |
|
5 | 5 | <tr> |
|
6 | 6 | |
|
7 | 7 | <th class="status" colspan="2"></th> |
|
8 | 8 | <th>${_('Commit')}</th> |
|
9 | 9 | <th>${_('Commit message')}</th> |
|
10 | 10 | <th>${_('Age')}</th> |
|
11 | 11 | <th>${_('Author')}</th> |
|
12 | 12 | <th>${_('Refs')}</th> |
|
13 | 13 | </tr> |
|
14 | 14 | %for cnt,cs in enumerate(c.repo_commits): |
|
15 | 15 | <tr class="parity${cnt%2}"> |
|
16 | 16 | |
|
17 | 17 | <td class="td-status"> |
|
18 | 18 | %if c.statuses.get(cs.raw_id): |
|
19 | 19 | <div class="changeset-status-ico shortlog"> |
|
20 | 20 | %if c.statuses.get(cs.raw_id)[2]: |
|
21 | 21 | <a class="tooltip" title="${_('Commit status: %s\nClick to open associated pull request #%s') % (c.statuses.get(cs.raw_id)[0], c.statuses.get(cs.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}"> |
|
22 | 22 | <div class="${'flag_status %s' % c.statuses.get(cs.raw_id)[0]}"></div> |
|
23 | 23 | </a> |
|
24 | 24 | %else: |
|
25 | 25 | <a class="tooltip" title="${_('Commit status: %s') % h.commit_status_lbl(c.statuses.get(cs.raw_id)[0])}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}"> |
|
26 | 26 | <div class="${'flag_status %s' % c.statuses.get(cs.raw_id)[0]}"></div> |
|
27 | 27 | </a> |
|
28 | 28 | %endif |
|
29 | 29 | </div> |
|
30 | 30 | %else: |
|
31 | 31 | <div class="tooltip flag_status not_reviewed" title="${_('Commit status: Not Reviewed')}"></div> |
|
32 | 32 | %endif |
|
33 | 33 | </td> |
|
34 | 34 | <td class="td-comments"> |
|
35 | 35 | %if c.comments.get(cs.raw_id,[]): |
|
36 | 36 | <a title="${_('Commit has comments')}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}"> |
|
37 |
<i class="icon-comment |
|
|
37 | <i class="icon-comment"></i> ${len(c.comments[cs.raw_id])} | |
|
38 | 38 | </a> |
|
39 | 39 | %endif |
|
40 | 40 | </td> |
|
41 | 41 | <td class="td-commit"> |
|
42 | 42 | <pre><a href="${h.url('changeset_home', repo_name=c.repo_name, revision=cs.raw_id)}">${h.show_id(cs)}</a></pre> |
|
43 | 43 | </td> |
|
44 | 44 | |
|
45 | 45 | <td class="td-description mid"> |
|
46 | 46 | <div class="log-container truncate-wrap"> |
|
47 | 47 | <div class="message truncate" id="c-${cs.raw_id}">${h.urlify_commit_message(cs.message, c.repo_name)}</div> |
|
48 | 48 | </div> |
|
49 | 49 | </td> |
|
50 | 50 | |
|
51 | 51 | <td class="td-time"> |
|
52 | 52 | ${h.age_component(cs.date)} |
|
53 | 53 | </td> |
|
54 | 54 | <td class="td-user author"> |
|
55 | 55 | ${base.gravatar_with_user(cs.author)} |
|
56 | 56 | </td> |
|
57 | 57 | |
|
58 | 58 | <td class="td-tags"> |
|
59 | 59 | <div class="autoexpand"> |
|
60 | 60 | %if h.is_hg(c.rhodecode_repo): |
|
61 | 61 | %for book in cs.bookmarks: |
|
62 | 62 | <span class="booktag tag" title="${_('Bookmark %s') % book}"> |
|
63 | 63 | <a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}"><i class="icon-bookmark"></i>${h.shorter(book)}</a> |
|
64 | 64 | </span> |
|
65 | 65 | %endfor |
|
66 | 66 | %endif |
|
67 | 67 | ## tags |
|
68 | 68 | %for tag in cs.tags: |
|
69 | 69 | <span class="tagtag tag" title="${_('Tag %s') % tag}"> |
|
70 | 70 | <a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}"><i class="icon-tag"></i>${h.shorter(tag)}</a> |
|
71 | 71 | </span> |
|
72 | 72 | %endfor |
|
73 | 73 | |
|
74 | 74 | ## branch |
|
75 | 75 | %if cs.branch: |
|
76 | 76 | <span class="branchtag tag" title="${_('Branch %s') % cs.branch}"> |
|
77 | 77 | <a href="${h.url('changelog_home',repo_name=c.repo_name,branch=cs.branch)}"><i class="icon-code-fork"></i>${h.shorter(cs.branch)}</a> |
|
78 | 78 | </span> |
|
79 | 79 | %endif |
|
80 | 80 | </div> |
|
81 | 81 | </td> |
|
82 | 82 | </tr> |
|
83 | 83 | %endfor |
|
84 | 84 | |
|
85 | 85 | </table> |
|
86 | 86 | |
|
87 | 87 | <script type="text/javascript"> |
|
88 | 88 | $(document).pjax('#shortlog_data .pager_link','#shortlog_data', {timeout: 2000, scrollTo: false }); |
|
89 | 89 | $(document).on('pjax:success', function(){ timeagoActivate(); }); |
|
90 | 90 | </script> |
|
91 | 91 | |
|
92 | 92 | <div class="pagination-wh pagination-left"> |
|
93 | 93 | ${c.repo_commits.pager('$link_previous ~2~ $link_next')} |
|
94 | 94 | </div> |
|
95 | 95 | %else: |
|
96 | 96 | |
|
97 | 97 | %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name): |
|
98 | 98 | <div class="quick_start"> |
|
99 | 99 | <div class="fieldset"> |
|
100 | 100 | <div class="left-label">${_('Add or upload files directly via RhodeCode:')}</div> |
|
101 | 101 | <div class="right-content"> |
|
102 | 102 | <div id="add_node_id" class="add_node"> |
|
103 | 103 | <a href="${h.url('files_add_home',repo_name=c.repo_name,revision=0,f_path='', anchor='edit')}" class="btn btn-default">${_('Add New File')}</a> |
|
104 | 104 | </div> |
|
105 | 105 | </div> |
|
106 | 106 | %endif |
|
107 | 107 | </div> |
|
108 | 108 | |
|
109 | 109 | %if not h.is_svn(c.rhodecode_repo): |
|
110 | 110 | <div class="fieldset"> |
|
111 | 111 | <div class="left-label">${_('Push new repo:')}</div> |
|
112 | 112 | <div class="right-content"> |
|
113 | 113 | <pre> |
|
114 | 114 | ${c.rhodecode_repo.alias} clone ${c.clone_repo_url} |
|
115 | 115 | ${c.rhodecode_repo.alias} add README # add first file |
|
116 | 116 | ${c.rhodecode_repo.alias} commit -m "Initial" # commit with message |
|
117 | 117 | ${c.rhodecode_repo.alias} push ${'origin master' if h.is_git(c.rhodecode_repo) else ''} # push changes back |
|
118 | 118 | </pre> |
|
119 | 119 | </div> |
|
120 | 120 | </div> |
|
121 | 121 | <div class="fieldset"> |
|
122 | 122 | <div class="left-label">${_('Existing repository?')}</div> |
|
123 | 123 | <div class="right-content"> |
|
124 | 124 | <pre> |
|
125 | 125 | %if h.is_git(c.rhodecode_repo): |
|
126 | 126 | git remote add origin ${c.clone_repo_url} |
|
127 | 127 | git push -u origin master |
|
128 | 128 | %else: |
|
129 | 129 | hg push ${c.clone_repo_url} |
|
130 | 130 | %endif |
|
131 | 131 | </pre> |
|
132 | 132 | </div> |
|
133 | 133 | </div> |
|
134 | 134 | %endif |
|
135 | 135 | </div> |
|
136 | 136 | %endif |
@@ -1,208 +1,209 b'' | |||
|
1 | 1 | ## -*- coding: utf-8 -*- |
|
2 | 2 | ## usage: |
|
3 | 3 | ## <%namespace name="comment" file="/changeset/changeset_file_comment.html"/> |
|
4 | 4 | ## ${comment.comment_block(comment)} |
|
5 | 5 | ## |
|
6 | 6 | <%namespace name="base" file="/base/base.html"/> |
|
7 | 7 | |
|
8 | 8 | <%def name="comment_block(comment, inline=False)"> |
|
9 | <% outdated_at_ver = comment.outdated_at_version(getattr(c, 'at_version', None)) %> | |
|
10 | ||
|
9 | 11 | <div class="comment |
|
10 | 12 | ${'comment-inline' if inline else ''} |
|
11 |
${'comment-outdated' if |
|
|
13 | ${'comment-outdated' if outdated_at_ver else 'comment-current'}" | |
|
12 | 14 | id="comment-${comment.comment_id}" |
|
13 | 15 | line="${comment.line_no}" |
|
14 | 16 | data-comment-id="${comment.comment_id}" |
|
15 |
style="${'display: none;' if |
|
|
17 | style="${'display: none;' if outdated_at_ver else ''}"> | |
|
16 | 18 | |
|
17 | 19 | <div class="meta"> |
|
18 | 20 | <div class="author"> |
|
19 | 21 | ${base.gravatar_with_user(comment.author.email, 16)} |
|
20 | 22 | </div> |
|
21 | 23 | <div class="date"> |
|
22 | 24 | ${h.age_component(comment.modified_at, time_is_local=True)} |
|
23 | 25 | </div> |
|
24 | 26 | <div class="status-change"> |
|
25 | 27 | % if comment.pull_request: |
|
26 | 28 | % if comment.outdated: |
|
27 | 29 | <a href="?version=${comment.pull_request_version_id}#comment-${comment.comment_id}"> |
|
28 | 30 | ${_('Outdated comment from pull request version {}').format(comment.pull_request_version_id)} |
|
29 | 31 | </a> |
|
30 | 32 | % else: |
|
31 | 33 | <a href="${h.url('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id)}"> |
|
32 | 34 | %if comment.status_change: |
|
33 | 35 | ${_('Vote on pull request #%s') % comment.pull_request.pull_request_id}: |
|
34 | 36 | %else: |
|
35 | 37 | ${_('Comment on pull request #%s') % comment.pull_request.pull_request_id} |
|
36 | 38 | %endif |
|
37 | 39 | </a> |
|
38 | 40 | % endif |
|
39 | 41 | % else: |
|
40 | 42 | % if comment.status_change: |
|
41 | 43 | ${_('Status change on commit')}: |
|
42 | 44 | % else: |
|
43 | 45 | ${_('Comment on commit')} |
|
44 | 46 | % endif |
|
45 | 47 | % endif |
|
46 | 48 | </div> |
|
47 | 49 | %if comment.status_change: |
|
48 | 50 | <div class="${'flag_status %s' % comment.status_change[0].status}"></div> |
|
49 | 51 | <div title="${_('Commit status')}" class="changeset-status-lbl"> |
|
50 | 52 | ${comment.status_change[0].status_lbl} |
|
51 | 53 | </div> |
|
52 | 54 | %endif |
|
53 | 55 | <a class="permalink" href="#comment-${comment.comment_id}"> ¶</a> |
|
54 | 56 | |
|
55 | ||
|
56 | 57 | <div class="comment-links-block"> |
|
57 | ||
|
58 | 58 | ## show delete comment if it's not a PR (regular comments) or it's PR that is not closed |
|
59 | ## only super-admin, repo admin OR comment owner can delete | |
|
60 | %if not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed()): | |
|
59 | ## only super-admin, repo admin OR comment owner can delete, also hide delete if currently viewed comment is outdated | |
|
60 | %if not outdated_at_ver and (not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed())): | |
|
61 | 61 | ## permissions to delete |
|
62 | 62 | %if h.HasPermissionAny('hg.admin')() or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id: |
|
63 | 63 | ## TODO: dan: add edit comment here |
|
64 | 64 | <a onclick="return Rhodecode.comments.deleteComment(this);" class="delete-comment"> ${_('Delete')}</a> |
|
65 | 65 | %else: |
|
66 | 66 | <button class="btn-link" disabled="disabled"> ${_('Delete')}</button> |
|
67 | 67 | %endif |
|
68 | 68 | %else: |
|
69 | 69 | <button class="btn-link" disabled="disabled"> ${_('Delete')}</button> |
|
70 | 70 | %endif |
|
71 | %if not comment.outdated_at_version(getattr(c, 'at_version', None)): | |
|
71 | ||
|
72 | %if not outdated_at_ver: | |
|
72 | 73 | | <a onclick="return Rhodecode.comments.prevComment(this);" class="prev-comment"> ${_('Prev')}</a> |
|
73 | 74 | | <a onclick="return Rhodecode.comments.nextComment(this);" class="next-comment"> ${_('Next')}</a> |
|
74 | 75 | %endif |
|
75 | 76 | |
|
76 | 77 | </div> |
|
77 | 78 | </div> |
|
78 | 79 | <div class="text"> |
|
79 | 80 | ${comment.render(mentions=True)|n} |
|
80 | 81 | </div> |
|
81 | 82 | |
|
82 | 83 | </div> |
|
83 | 84 | </%def> |
|
84 | 85 | ## generate main comments |
|
85 | 86 | <%def name="generate_comments(include_pull_request=False, is_pull_request=False)"> |
|
86 | 87 | <div id="comments"> |
|
87 | 88 | %for comment in c.comments: |
|
88 | 89 | <div id="comment-tr-${comment.comment_id}"> |
|
89 | 90 | ## only render comments that are not from pull request, or from |
|
90 | 91 | ## pull request and a status change |
|
91 | 92 | %if not comment.pull_request or (comment.pull_request and comment.status_change) or include_pull_request: |
|
92 | 93 | ${comment_block(comment)} |
|
93 | 94 | %endif |
|
94 | 95 | </div> |
|
95 | 96 | %endfor |
|
96 | 97 | ## to anchor ajax comments |
|
97 | 98 | <div id="injected_page_comments"></div> |
|
98 | 99 | </div> |
|
99 | 100 | </%def> |
|
100 | 101 | |
|
101 | 102 | ## MAIN COMMENT FORM |
|
102 | 103 | <%def name="comments(post_url, cur_status, is_pull_request=False, is_compare=False, change_status=True, form_extras=None)"> |
|
103 | 104 | |
|
104 | 105 | %if is_compare: |
|
105 | 106 | <% form_id = "comments_form_compare" %> |
|
106 | 107 | %else: |
|
107 | 108 | <% form_id = "comments_form" %> |
|
108 | 109 | %endif |
|
109 | 110 | |
|
110 | 111 | |
|
111 | 112 | %if is_pull_request: |
|
112 | 113 | <div class="pull-request-merge"> |
|
113 | 114 | %if c.allowed_to_merge: |
|
114 | 115 | <div class="pull-request-wrap"> |
|
115 | 116 | <div class="pull-right"> |
|
116 | 117 | ${h.secure_form(url('pullrequest_merge', repo_name=c.repo_name, pull_request_id=c.pull_request.pull_request_id), id='merge_pull_request_form')} |
|
117 | 118 | <span data-role="merge-message">${c.pr_merge_msg} ${c.approval_msg if c.approval_msg else ''}</span> |
|
118 | 119 | <% merge_disabled = ' disabled' if c.pr_merge_status is False else '' %> |
|
119 | 120 | <input type="submit" id="merge_pull_request" value="${_('Merge Pull Request')}" class="btn${merge_disabled}"${merge_disabled}> |
|
120 | 121 | ${h.end_form()} |
|
121 | 122 | </div> |
|
122 | 123 | </div> |
|
123 | 124 | %else: |
|
124 | 125 | <div class="pull-request-wrap"> |
|
125 | 126 | <div class="pull-right"> |
|
126 | 127 | <span>${c.pr_merge_msg} ${c.approval_msg if c.approval_msg else ''}</span> |
|
127 | 128 | </div> |
|
128 | 129 | </div> |
|
129 | 130 | %endif |
|
130 | 131 | </div> |
|
131 | 132 | %endif |
|
132 | 133 | <div class="comments"> |
|
133 | 134 | %if c.rhodecode_user.username != h.DEFAULT_USER: |
|
134 | 135 | <div class="comment-form ac"> |
|
135 | 136 | ${h.secure_form(post_url, id_=form_id)} |
|
136 | 137 | <div id="edit-container" class="clearfix"> |
|
137 | 138 | <div class="comment-title pull-left"> |
|
138 | 139 | %if is_pull_request: |
|
139 | 140 | ${(_('Create a comment on this Pull Request.'))} |
|
140 | 141 | %elif is_compare: |
|
141 | 142 | ${(_('Create comments on this Commit range.'))} |
|
142 | 143 | %else: |
|
143 | 144 | ${(_('Create a comment on this Commit.'))} |
|
144 | 145 | %endif |
|
145 | 146 | </div> |
|
146 | 147 | <div class="comment-help pull-right"> |
|
147 | 148 | ${(_('Comments parsed using %s syntax with %s support.') % ( |
|
148 | 149 | ('<a href="%s">%s</a>' % (h.url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())), |
|
149 | 150 | ('<span class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user')) |
|
150 | 151 | ) |
|
151 | 152 | )|n |
|
152 | 153 | } |
|
153 | 154 | </div> |
|
154 | 155 | <div style="clear: both"></div> |
|
155 | 156 | ${h.textarea('text', class_="comment-block-ta")} |
|
156 | 157 | </div> |
|
157 | 158 | |
|
158 | 159 | <div id="preview-container" class="clearfix" style="display: none;"> |
|
159 | 160 | <div class="comment-title"> |
|
160 | 161 | ${_('Comment preview')} |
|
161 | 162 | </div> |
|
162 | 163 | <div id="preview-box" class="preview-box"></div> |
|
163 | 164 | </div> |
|
164 | 165 | |
|
165 | 166 | <div id="comment_form_extras"> |
|
166 | 167 | %if form_extras and isinstance(form_extras, (list, tuple)): |
|
167 | 168 | % for form_ex_el in form_extras: |
|
168 | 169 | ${form_ex_el|n} |
|
169 | 170 | % endfor |
|
170 | 171 | %endif |
|
171 | 172 | </div> |
|
172 | 173 | <div class="comment-footer"> |
|
173 | 174 | %if change_status: |
|
174 | 175 | <div class="status_box"> |
|
175 | 176 | <select id="change_status" name="changeset_status"> |
|
176 | 177 | <option></option> # Placeholder |
|
177 | 178 | %for status,lbl in c.commit_statuses: |
|
178 | 179 | <option value="${status}" data-status="${status}">${lbl}</option> |
|
179 | 180 | %if is_pull_request and change_status and status in ('approved', 'rejected'): |
|
180 | 181 | <option value="${status}_closed" data-status="${status}">${lbl} & ${_('Closed')}</option> |
|
181 | 182 | %endif |
|
182 | 183 | %endfor |
|
183 | 184 | </select> |
|
184 | 185 | </div> |
|
185 | 186 | %endif |
|
186 | 187 | <div class="action-buttons"> |
|
187 | 188 | <button id="preview-btn" class="btn btn-secondary">${_('Preview')}</button> |
|
188 | 189 | <button id="edit-btn" class="btn btn-secondary" style="display:none;">${_('Edit')}</button> |
|
189 | 190 | <div class="comment-button">${h.submit('save', _('Comment'), class_="btn btn-success comment-button-input")}</div> |
|
190 | 191 | </div> |
|
191 | 192 | </div> |
|
192 | 193 | ${h.end_form()} |
|
193 | 194 | </div> |
|
194 | 195 | %endif |
|
195 | 196 | </div> |
|
196 | 197 | <script> |
|
197 | 198 | // init active elements of commentForm |
|
198 | 199 | var commitId = templateContext.commit_data.commit_id; |
|
199 | 200 | var pullRequestId = templateContext.pull_request_data.pull_request_id; |
|
200 | 201 | var lineNo; |
|
201 | 202 | |
|
202 | 203 | var mainCommentForm = new CommentForm( |
|
203 | 204 | "#${form_id}", commitId, pullRequestId, lineNo, true); |
|
204 | 205 | |
|
205 | 206 | mainCommentForm.initStatusChangeSelector(); |
|
206 | 207 | bindToggleButtons(); |
|
207 | 208 | </script> |
|
208 | 209 | </%def> |
@@ -1,609 +1,691 b'' | |||
|
1 | 1 | <%def name="diff_line_anchor(filename, line, type)"><% |
|
2 | 2 | return '%s_%s_%i' % (h.safeid(filename), type, line) |
|
3 | 3 | %></%def> |
|
4 | 4 | |
|
5 | 5 | <%def name="action_class(action)"><% |
|
6 | 6 | return { |
|
7 | 7 | '-': 'cb-deletion', |
|
8 | 8 | '+': 'cb-addition', |
|
9 | 9 | ' ': 'cb-context', |
|
10 | 10 | }.get(action, 'cb-empty') |
|
11 | 11 | %></%def> |
|
12 | 12 | |
|
13 | 13 | <%def name="op_class(op_id)"><% |
|
14 | 14 | return { |
|
15 | 15 | DEL_FILENODE: 'deletion', # file deleted |
|
16 | 16 | BIN_FILENODE: 'warning' # binary diff hidden |
|
17 | 17 | }.get(op_id, 'addition') |
|
18 | 18 | %></%def> |
|
19 | 19 | |
|
20 | 20 | <%def name="link_for(**kw)"><% |
|
21 | 21 | new_args = request.GET.mixed() |
|
22 | 22 | new_args.update(kw) |
|
23 | 23 | return h.url('', **new_args) |
|
24 | 24 | %></%def> |
|
25 | 25 | |
|
26 | 26 | <%def name="render_diffset(diffset, commit=None, |
|
27 | 27 | |
|
28 | 28 | # collapse all file diff entries when there are more than this amount of files in the diff |
|
29 | 29 | collapse_when_files_over=20, |
|
30 | 30 | |
|
31 | 31 | # collapse lines in the diff when more than this amount of lines changed in the file diff |
|
32 | 32 | lines_changed_limit=500, |
|
33 | 33 | |
|
34 | 34 | # add a ruler at to the output |
|
35 | 35 | ruler_at_chars=0, |
|
36 | 36 | |
|
37 | 37 | # show inline comments |
|
38 | 38 | use_comments=False, |
|
39 | 39 | |
|
40 | 40 | # disable new comments |
|
41 | 41 | disable_new_comments=False, |
|
42 | 42 | |
|
43 | # special file-comments that were deleted in previous versions | |
|
44 | # it's used for showing outdated comments for deleted files in a PR | |
|
45 | deleted_files_comments=None | |
|
46 | ||
|
43 | 47 | )"> |
|
44 | 48 | |
|
45 | 49 | %if use_comments: |
|
46 | 50 | <div id="cb-comments-inline-container-template" class="js-template"> |
|
47 | 51 | ${inline_comments_container([])} |
|
48 | 52 | </div> |
|
49 | 53 | <div class="js-template" id="cb-comment-inline-form-template"> |
|
50 | 54 | <div class="comment-inline-form ac"> |
|
51 | 55 | %if c.rhodecode_user.username != h.DEFAULT_USER: |
|
52 | 56 | ${h.form('#', method='get')} |
|
53 | 57 | <div id="edit-container_{1}" class="clearfix"> |
|
54 | 58 | <div class="comment-title pull-left"> |
|
55 | 59 | ${_('Create a comment on line {1}.')} |
|
56 | 60 | </div> |
|
57 | 61 | <div class="comment-help pull-right"> |
|
58 | 62 | ${(_('Comments parsed using %s syntax with %s support.') % ( |
|
59 | 63 | ('<a href="%s">%s</a>' % (h.url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())), |
|
60 | 64 | ('<span class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user')) |
|
61 | 65 | ) |
|
62 | 66 | )|n |
|
63 | 67 | } |
|
64 | 68 | </div> |
|
65 | 69 | <div style="clear: both"></div> |
|
66 | 70 | <textarea id="text_{1}" name="text" class="comment-block-ta ac-input"></textarea> |
|
67 | 71 | </div> |
|
68 | 72 | <div id="preview-container_{1}" class="clearfix" style="display: none;"> |
|
69 | 73 | <div class="comment-help"> |
|
70 | 74 | ${_('Comment preview')} |
|
71 | 75 | </div> |
|
72 | 76 | <div id="preview-box_{1}" class="preview-box"></div> |
|
73 | 77 | </div> |
|
74 | 78 | <div class="comment-footer"> |
|
75 | 79 | <div class="action-buttons"> |
|
76 | 80 | <input type="hidden" name="f_path" value="{0}"> |
|
77 | 81 | <input type="hidden" name="line" value="{1}"> |
|
78 | 82 | <button id="preview-btn_{1}" class="btn btn-secondary">${_('Preview')}</button> |
|
79 | 83 | <button id="edit-btn_{1}" class="btn btn-secondary" style="display: none;">${_('Edit')}</button> |
|
80 | 84 | ${h.submit('save', _('Comment'), class_='btn btn-success save-inline-form')} |
|
81 | 85 | </div> |
|
82 | 86 | <div class="comment-button"> |
|
83 | 87 | <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);"> |
|
84 | 88 | ${_('Cancel')} |
|
85 | 89 | </button> |
|
86 | 90 | </div> |
|
87 | 91 | ${h.end_form()} |
|
88 | 92 | </div> |
|
89 | 93 | %else: |
|
90 | 94 | ${h.form('', class_='inline-form comment-form-login', method='get')} |
|
91 | 95 | <div class="pull-left"> |
|
92 | 96 | <div class="comment-help pull-right"> |
|
93 | 97 | ${_('You need to be logged in to comment.')} <a href="${h.route_path('login', _query={'came_from': h.url.current()})}">${_('Login now')}</a> |
|
94 | 98 | </div> |
|
95 | 99 | </div> |
|
96 | 100 | <div class="comment-button pull-right"> |
|
97 | 101 | <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);"> |
|
98 | 102 | ${_('Cancel')} |
|
99 | 103 | </button> |
|
100 | 104 | </div> |
|
101 | 105 | <div class="clearfix"></div> |
|
102 | 106 | ${h.end_form()} |
|
103 | 107 | %endif |
|
104 | 108 | </div> |
|
105 | 109 | </div> |
|
106 | 110 | |
|
107 | 111 | %endif |
|
108 | 112 | <% |
|
109 | 113 | collapse_all = len(diffset.files) > collapse_when_files_over |
|
110 | 114 | %> |
|
111 | 115 | |
|
112 | 116 | %if c.diffmode == 'sideside': |
|
113 | 117 | <style> |
|
114 | 118 | .wrapper { |
|
115 | 119 | max-width: 1600px !important; |
|
116 | 120 | } |
|
117 | 121 | </style> |
|
118 | 122 | %endif |
|
119 | 123 | %if ruler_at_chars: |
|
120 | 124 | <style> |
|
121 | 125 | .diff table.cb .cb-content:after { |
|
122 | 126 | content: ""; |
|
123 | 127 | border-left: 1px solid blue; |
|
124 | 128 | position: absolute; |
|
125 | 129 | top: 0; |
|
126 | 130 | height: 18px; |
|
127 | 131 | opacity: .2; |
|
128 | 132 | z-index: 10; |
|
129 | ## +5 to account for diff action (+/-) | |
|
133 | //## +5 to account for diff action (+/-) | |
|
130 | 134 | left: ${ruler_at_chars + 5}ch; |
|
131 | 135 | </style> |
|
132 | 136 | %endif |
|
133 | 137 | <div class="diffset ${disable_new_comments and 'diffset-comments-disabled'}"> |
|
134 | 138 | <div class="diffset-heading ${diffset.limited_diff and 'diffset-heading-warning' or ''}"> |
|
135 | 139 | %if commit: |
|
136 | 140 | <div class="pull-right"> |
|
137 | 141 | <a class="btn tooltip" title="${_('Browse Files at revision {}').format(commit.raw_id)}" href="${h.url('files_home',repo_name=diffset.repo_name, revision=commit.raw_id, f_path='')}"> |
|
138 | 142 | ${_('Browse Files')} |
|
139 | 143 | </a> |
|
140 | 144 | </div> |
|
141 | 145 | %endif |
|
142 | 146 | <h2 class="clearinner"> |
|
143 | 147 | %if commit: |
|
144 | 148 | <a class="tooltip revision" title="${h.tooltip(commit.message)}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id)}">${'r%s:%s' % (commit.revision,h.short_id(commit.raw_id))}</a> - |
|
145 | 149 | ${h.age_component(commit.date)} - |
|
146 | 150 | %endif |
|
147 | 151 | %if diffset.limited_diff: |
|
148 | ${_('The requested commit is too big and content was truncated.')} | |
|
152 | ${_('The requested commit is too big and content was truncated.')} | |
|
149 | 153 | |
|
150 | 154 | ${ungettext('%(num)s file changed.', '%(num)s files changed.', diffset.changed_files) % {'num': diffset.changed_files}} |
|
151 | 155 | <a href="${link_for(fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a> |
|
152 | 156 | %else: |
|
153 | 157 | ${ungettext('%(num)s file changed: %(linesadd)s inserted, ''%(linesdel)s deleted', |
|
154 | 158 | '%(num)s files changed: %(linesadd)s inserted, %(linesdel)s deleted', diffset.changed_files) % {'num': diffset.changed_files, 'linesadd': diffset.lines_added, 'linesdel': diffset.lines_deleted}} |
|
155 | 159 | %endif |
|
160 | ||
|
161 | <% at_ver = getattr(c, 'at_version_num', None) %> | |
|
162 | % if at_ver: | |
|
163 | <div class="pull-right"> | |
|
164 | ${_('Changes at version %d') % at_ver} | |
|
165 | </div> | |
|
166 | % endif | |
|
167 | ||
|
156 | 168 | </h2> |
|
157 | 169 | </div> |
|
158 | 170 | |
|
159 | 171 | %if not diffset.files: |
|
160 | 172 | <p class="empty_data">${_('No files')}</p> |
|
161 | 173 | %endif |
|
162 | 174 | |
|
163 | 175 | <div class="filediffs"> |
|
164 | 176 | %for i, filediff in enumerate(diffset.files): |
|
165 | 177 | |
|
166 | 178 | <% |
|
167 | 179 | lines_changed = filediff['patch']['stats']['added'] + filediff['patch']['stats']['deleted'] |
|
168 | 180 | over_lines_changed_limit = lines_changed > lines_changed_limit |
|
169 | 181 | %> |
|
170 | 182 | <input ${collapse_all and 'checked' or ''} class="filediff-collapse-state" id="filediff-collapse-${id(filediff)}" type="checkbox"> |
|
171 | 183 | <div |
|
172 | 184 | class="filediff" |
|
173 | 185 | data-f-path="${filediff['patch']['filename']}" |
|
174 | 186 | id="a_${h.FID('', filediff['patch']['filename'])}"> |
|
175 | 187 | <label for="filediff-collapse-${id(filediff)}" class="filediff-heading"> |
|
176 | 188 | <div class="filediff-collapse-indicator"></div> |
|
177 | 189 | ${diff_ops(filediff)} |
|
178 | 190 | </label> |
|
179 | 191 | ${diff_menu(filediff, use_comments=use_comments)} |
|
180 | 192 | <table class="cb cb-diff-${c.diffmode} code-highlight ${over_lines_changed_limit and 'cb-collapsed' or ''}"> |
|
181 | 193 | %if not filediff.hunks: |
|
182 | 194 | %for op_id, op_text in filediff['patch']['stats']['ops'].items(): |
|
183 | 195 | <tr> |
|
184 |
<td class="cb-text cb-${op_class(op_id)}" ${c.diffmode == 'unified' and 'colspan= |
|
|
196 | <td class="cb-text cb-${op_class(op_id)}" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=6'}> | |
|
185 | 197 | %if op_id == DEL_FILENODE: |
|
186 | 198 | ${_('File was deleted')} |
|
187 | 199 | %elif op_id == BIN_FILENODE: |
|
188 | 200 | ${_('Binary file hidden')} |
|
189 | 201 | %else: |
|
190 | 202 | ${op_text} |
|
191 | 203 | %endif |
|
192 | 204 | </td> |
|
193 | 205 | </tr> |
|
194 | 206 | %endfor |
|
195 | 207 | %endif |
|
196 | %if over_lines_changed_limit: | |
|
197 | <tr class="cb-warning cb-collapser"> | |
|
198 | <td class="cb-text" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=6'}> | |
|
199 | ${_('This diff has been collapsed as it changes many lines, (%i lines changed)' % lines_changed)} | |
|
200 | <a href="#" class="cb-expand" | |
|
201 | onclick="$(this).closest('table').removeClass('cb-collapsed'); return false;">${_('Show them')} | |
|
202 | </a> | |
|
203 | <a href="#" class="cb-collapse" | |
|
204 | onclick="$(this).closest('table').addClass('cb-collapsed'); return false;">${_('Hide them')} | |
|
205 | </a> | |
|
206 | </td> | |
|
207 | </tr> | |
|
208 | %endif | |
|
209 | 208 | %if filediff.patch['is_limited_diff']: |
|
210 | 209 | <tr class="cb-warning cb-collapser"> |
|
211 | 210 | <td class="cb-text" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=6'}> |
|
212 | 211 | ${_('The requested commit is too big and content was truncated.')} <a href="${link_for(fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a> |
|
213 | 212 | </td> |
|
214 | 213 | </tr> |
|
214 | %else: | |
|
215 | %if over_lines_changed_limit: | |
|
216 | <tr class="cb-warning cb-collapser"> | |
|
217 | <td class="cb-text" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=6'}> | |
|
218 | ${_('This diff has been collapsed as it changes many lines, (%i lines changed)' % lines_changed)} | |
|
219 | <a href="#" class="cb-expand" | |
|
220 | onclick="$(this).closest('table').removeClass('cb-collapsed'); return false;">${_('Show them')} | |
|
221 | </a> | |
|
222 | <a href="#" class="cb-collapse" | |
|
223 | onclick="$(this).closest('table').addClass('cb-collapsed'); return false;">${_('Hide them')} | |
|
224 | </a> | |
|
225 | </td> | |
|
226 | </tr> | |
|
227 | %endif | |
|
215 | 228 | %endif |
|
229 | ||
|
216 | 230 | %for hunk in filediff.hunks: |
|
217 | 231 | <tr class="cb-hunk"> |
|
218 | 232 | <td ${c.diffmode == 'unified' and 'colspan=3' or ''}> |
|
219 | 233 | ## TODO: dan: add ajax loading of more context here |
|
220 | 234 | ## <a href="#"> |
|
221 | 235 | <i class="icon-more"></i> |
|
222 | 236 | ## </a> |
|
223 | 237 | </td> |
|
224 | 238 | <td ${c.diffmode == 'sideside' and 'colspan=5' or ''}> |
|
225 | 239 | @@ |
|
226 | 240 | -${hunk.source_start},${hunk.source_length} |
|
227 | 241 | +${hunk.target_start},${hunk.target_length} |
|
228 | 242 | ${hunk.section_header} |
|
229 | 243 | </td> |
|
230 | 244 | </tr> |
|
231 | 245 | %if c.diffmode == 'unified': |
|
232 | 246 | ${render_hunk_lines_unified(hunk, use_comments=use_comments)} |
|
233 | 247 | %elif c.diffmode == 'sideside': |
|
234 | 248 | ${render_hunk_lines_sideside(hunk, use_comments=use_comments)} |
|
235 | 249 | %else: |
|
236 | 250 | <tr class="cb-line"> |
|
237 | 251 | <td>unknown diff mode</td> |
|
238 | 252 | </tr> |
|
239 | 253 | %endif |
|
240 | 254 | %endfor |
|
241 | 255 | |
|
242 | % for lineno, comments in filediff.left_comments.items(): | |
|
256 | ## outdated comments that do not fit into currently displayed lines | |
|
257 | % for lineno, comments in filediff.left_comments.items(): | |
|
258 | ||
|
259 | %if c.diffmode == 'unified': | |
|
260 | <tr class="cb-line"> | |
|
261 | <td class="cb-data cb-context"></td> | |
|
262 | <td class="cb-lineno cb-context"></td> | |
|
263 | <td class="cb-lineno cb-context"></td> | |
|
264 | <td class="cb-content cb-context"> | |
|
265 | ${inline_comments_container(comments)} | |
|
266 | </td> | |
|
267 | </tr> | |
|
268 | %elif c.diffmode == 'sideside': | |
|
269 | <tr class="cb-line"> | |
|
270 | <td class="cb-data cb-context"></td> | |
|
271 | <td class="cb-lineno cb-context"></td> | |
|
272 | <td class="cb-content cb-context"></td> | |
|
273 | ||
|
274 | <td class="cb-data cb-context"></td> | |
|
275 | <td class="cb-lineno cb-context"></td> | |
|
276 | <td class="cb-content cb-context"> | |
|
277 | ${inline_comments_container(comments)} | |
|
278 | </td> | |
|
279 | </tr> | |
|
280 | %endif | |
|
281 | ||
|
282 | % endfor | |
|
283 | ||
|
284 | </table> | |
|
285 | </div> | |
|
286 | %endfor | |
|
287 | ||
|
288 | ## outdated comments that are made for a file that has been deleted | |
|
289 | % for filename, comments_dict in (deleted_files_comments or {}).items(): | |
|
243 | 290 | |
|
244 | %if c.diffmode == 'unified': | |
|
245 | <tr class="cb-line"> | |
|
246 | <td class="cb-data cb-context"></td> | |
|
247 | <td class="cb-lineno cb-context"></td> | |
|
248 |
< |
|
|
249 |
< |
|
|
250 | ${inline_comments_container(comments)} | |
|
251 |
</ |
|
|
252 |
</ |
|
|
253 | %elif c.diffmode == 'sideside': | |
|
254 | <tr class="cb-line"> | |
|
291 | <div class="filediffs filediff-outdated" style="display: none"> | |
|
292 | <input ${collapse_all and 'checked' or ''} class="filediff-collapse-state" id="filediff-collapse-${id(filename)}" type="checkbox"> | |
|
293 | <div class="filediff" data-f-path="${filename}" id="a_${h.FID('', filename)}"> | |
|
294 | <label for="filediff-collapse-${id(filename)}" class="filediff-heading"> | |
|
295 | <div class="filediff-collapse-indicator"></div> | |
|
296 | <span class="pill"> | |
|
297 | ## file was deleted | |
|
298 | <strong>${filename}</strong> | |
|
299 | </span> | |
|
300 | <span class="pill-group" style="float: left"> | |
|
301 | ## file op, doesn't need translation | |
|
302 | <span class="pill" op="removed">removed in this version</span> | |
|
303 | </span> | |
|
304 | <a class="pill filediff-anchor" href="#a_${h.FID('', filename)}">ΒΆ</a> | |
|
305 | <span class="pill-group" style="float: right"> | |
|
306 | <span class="pill" op="deleted">-${comments_dict['stats']}</span> | |
|
307 | </span> | |
|
308 | </label> | |
|
309 | ||
|
310 | <table class="cb cb-diff-${c.diffmode} code-highlight ${over_lines_changed_limit and 'cb-collapsed' or ''}"> | |
|
311 | <tr> | |
|
312 | % if c.diffmode == 'unified': | |
|
313 | <td></td> | |
|
314 | %endif | |
|
315 | ||
|
316 | <td></td> | |
|
317 | <td class="cb-text cb-${op_class(BIN_FILENODE)}" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=5'}> | |
|
318 | ${_('File was deleted in this version, and outdated comments were made on it')} | |
|
319 | </td> | |
|
320 | </tr> | |
|
321 | %if c.diffmode == 'unified': | |
|
322 | <tr class="cb-line"> | |
|
323 | <td class="cb-data cb-context"></td> | |
|
324 | <td class="cb-lineno cb-context"></td> | |
|
325 | <td class="cb-lineno cb-context"></td> | |
|
326 | <td class="cb-content cb-context"> | |
|
327 | ${inline_comments_container(comments_dict['comments'])} | |
|
328 | </td> | |
|
329 | </tr> | |
|
330 | %elif c.diffmode == 'sideside': | |
|
331 | <tr class="cb-line"> | |
|
255 | 332 | <td class="cb-data cb-context"></td> |
|
256 | 333 | <td class="cb-lineno cb-context"></td> |
|
257 | 334 | <td class="cb-content cb-context"></td> |
|
258 | 335 | |
|
259 | 336 | <td class="cb-data cb-context"></td> |
|
260 | 337 | <td class="cb-lineno cb-context"></td> |
|
261 | 338 | <td class="cb-content cb-context"> |
|
262 | ${inline_comments_container(comments)} | |
|
339 | ${inline_comments_container(comments_dict['comments'])} | |
|
263 | 340 | </td> |
|
264 | </tr> | |
|
265 | %endif | |
|
341 | </tr> | |
|
342 | %endif | |
|
343 | </table> | |
|
344 | </div> | |
|
345 | </div> | |
|
346 | % endfor | |
|
266 | 347 | |
|
267 | % endfor | |
|
268 | ||
|
269 | </table> | |
|
270 | </div> | |
|
271 | %endfor | |
|
272 | </div> | |
|
273 | 348 | </div> |
|
274 | 349 | </%def> |
|
275 | 350 | |
|
276 | 351 | <%def name="diff_ops(filediff)"> |
|
277 | 352 | <% |
|
278 | 353 | stats = filediff['patch']['stats'] |
|
279 | 354 | from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \ |
|
280 | 355 | MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE |
|
281 | 356 | %> |
|
282 | 357 | <span class="pill"> |
|
283 | 358 | %if filediff.source_file_path and filediff.target_file_path: |
|
284 | 359 | %if filediff.source_file_path != filediff.target_file_path: # file was renamed |
|
285 | 360 | <strong>${filediff.target_file_path}</strong> β¬ <del>${filediff.source_file_path}</del> |
|
286 | 361 | %else: |
|
287 | 362 | ## file was modified |
|
288 | 363 | <strong>${filediff.source_file_path}</strong> |
|
289 | 364 | %endif |
|
290 | 365 | %else: |
|
291 | 366 | %if filediff.source_file_path: |
|
292 | 367 | ## file was deleted |
|
293 | 368 | <strong>${filediff.source_file_path}</strong> |
|
294 | 369 | %else: |
|
295 | 370 | ## file was added |
|
296 | 371 | <strong>${filediff.target_file_path}</strong> |
|
297 | 372 | %endif |
|
298 | 373 | %endif |
|
299 | 374 | </span> |
|
300 | 375 | <span class="pill-group" style="float: left"> |
|
301 | 376 | %if filediff.patch['is_limited_diff']: |
|
302 | 377 | <span class="pill tooltip" op="limited" title="The stats for this diff are not complete">limited diff</span> |
|
303 | 378 | %endif |
|
304 | 379 | %if RENAMED_FILENODE in stats['ops']: |
|
305 | 380 | <span class="pill" op="renamed">renamed</span> |
|
306 | 381 | %endif |
|
307 | 382 | |
|
308 | 383 | %if NEW_FILENODE in stats['ops']: |
|
309 | 384 | <span class="pill" op="created">created</span> |
|
310 | 385 | %if filediff['target_mode'].startswith('120'): |
|
311 | 386 | <span class="pill" op="symlink">symlink</span> |
|
312 | 387 | %else: |
|
313 | 388 | <span class="pill" op="mode">${nice_mode(filediff['target_mode'])}</span> |
|
314 | 389 | %endif |
|
315 | 390 | %endif |
|
316 | 391 | |
|
317 | 392 | %if DEL_FILENODE in stats['ops']: |
|
318 | 393 | <span class="pill" op="removed">removed</span> |
|
319 | 394 | %endif |
|
320 | 395 | |
|
321 | 396 | %if CHMOD_FILENODE in stats['ops']: |
|
322 | 397 | <span class="pill" op="mode"> |
|
323 | 398 | ${nice_mode(filediff['source_mode'])} β‘ ${nice_mode(filediff['target_mode'])} |
|
324 | 399 | </span> |
|
325 | 400 | %endif |
|
326 | 401 | </span> |
|
327 | 402 | |
|
328 | 403 | <a class="pill filediff-anchor" href="#a_${h.FID('', filediff.patch['filename'])}">ΒΆ</a> |
|
329 | 404 | |
|
330 | 405 | <span class="pill-group" style="float: right"> |
|
331 | 406 | %if BIN_FILENODE in stats['ops']: |
|
332 | 407 | <span class="pill" op="binary">binary</span> |
|
333 | 408 | %if MOD_FILENODE in stats['ops']: |
|
334 | 409 | <span class="pill" op="modified">modified</span> |
|
335 | 410 | %endif |
|
336 | 411 | %endif |
|
337 | 412 | %if stats['added']: |
|
338 | 413 | <span class="pill" op="added">+${stats['added']}</span> |
|
339 | 414 | %endif |
|
340 | 415 | %if stats['deleted']: |
|
341 | 416 | <span class="pill" op="deleted">-${stats['deleted']}</span> |
|
342 | 417 | %endif |
|
343 | 418 | </span> |
|
344 | 419 | |
|
345 | 420 | </%def> |
|
346 | 421 | |
|
347 | 422 | <%def name="nice_mode(filemode)"> |
|
348 | 423 | ${filemode.startswith('100') and filemode[3:] or filemode} |
|
349 | 424 | </%def> |
|
350 | 425 | |
|
351 | 426 | <%def name="diff_menu(filediff, use_comments=False)"> |
|
352 | 427 | <div class="filediff-menu"> |
|
353 | 428 | %if filediff.diffset.source_ref: |
|
354 | 429 | %if filediff.patch['operation'] in ['D', 'M']: |
|
355 | 430 | <a |
|
356 | 431 | class="tooltip" |
|
357 | 432 | href="${h.url('files_home',repo_name=filediff.diffset.repo_name,f_path=filediff.source_file_path,revision=filediff.diffset.source_ref)}" |
|
358 | 433 | title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}" |
|
359 | 434 | > |
|
360 | 435 | ${_('Show file before')} |
|
361 | </a> | |
|
436 | </a> | | |
|
362 | 437 | %else: |
|
363 | 438 | <span |
|
364 | 439 | class="tooltip" |
|
365 | 440 | title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}" |
|
366 | 441 | > |
|
367 | 442 | ${_('Show file before')} |
|
368 | </span> | |
|
443 | </span> | | |
|
369 | 444 | %endif |
|
370 | 445 | %if filediff.patch['operation'] in ['A', 'M']: |
|
371 | 446 | <a |
|
372 | 447 | class="tooltip" |
|
373 | 448 | href="${h.url('files_home',repo_name=filediff.diffset.source_repo_name,f_path=filediff.target_file_path,revision=filediff.diffset.target_ref)}" |
|
374 | 449 | title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}" |
|
375 | 450 | > |
|
376 | 451 | ${_('Show file after')} |
|
377 | </a> | |
|
452 | </a> | | |
|
378 | 453 | %else: |
|
379 | 454 | <span |
|
380 | 455 | class="tooltip" |
|
381 | 456 | title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}" |
|
382 | 457 | > |
|
383 | 458 | ${_('Show file after')} |
|
384 | </span> | |
|
459 | </span> | | |
|
385 | 460 | %endif |
|
386 | 461 | <a |
|
387 | 462 | class="tooltip" |
|
388 | 463 | title="${h.tooltip(_('Raw diff'))}" |
|
389 | 464 | href="${h.url('files_diff_home',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path,diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='raw')}" |
|
390 | 465 | > |
|
391 | 466 | ${_('Raw diff')} |
|
392 | </a> | |
|
467 | </a> | | |
|
393 | 468 | <a |
|
394 | 469 | class="tooltip" |
|
395 | 470 | title="${h.tooltip(_('Download diff'))}" |
|
396 | 471 | href="${h.url('files_diff_home',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path,diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='download')}" |
|
397 | 472 | > |
|
398 | 473 | ${_('Download diff')} |
|
399 | 474 | </a> |
|
475 | % if use_comments: | |
|
476 | | | |
|
477 | % endif | |
|
400 | 478 | |
|
401 | 479 | ## TODO: dan: refactor ignorews_url and context_url into the diff renderer same as diffmode=unified/sideside. Also use ajax to load more context (by clicking hunks) |
|
402 | 480 | %if hasattr(c, 'ignorews_url'): |
|
403 | 481 | ${c.ignorews_url(request.GET, h.FID('', filediff['patch']['filename']))} |
|
404 | 482 | %endif |
|
405 | 483 | %if hasattr(c, 'context_url'): |
|
406 | 484 | ${c.context_url(request.GET, h.FID('', filediff['patch']['filename']))} |
|
407 | 485 | %endif |
|
408 | 486 | |
|
409 | ||
|
410 | 487 | %if use_comments: |
|
411 | 488 | <a href="#" onclick="return Rhodecode.comments.toggleComments(this);"> |
|
412 | 489 | <span class="show-comment-button">${_('Show comments')}</span><span class="hide-comment-button">${_('Hide comments')}</span> |
|
413 | 490 | </a> |
|
414 | 491 | %endif |
|
415 | 492 | %endif |
|
416 | 493 | </div> |
|
417 | 494 | </%def> |
|
418 | 495 | |
|
419 | 496 | |
|
420 | 497 | <%namespace name="commentblock" file="/changeset/changeset_file_comment.html"/> |
|
421 | 498 | <%def name="inline_comments_container(comments)"> |
|
422 | 499 | <div class="inline-comments"> |
|
423 | 500 | %for comment in comments: |
|
424 | 501 | ${commentblock.comment_block(comment, inline=True)} |
|
425 | 502 | %endfor |
|
426 | 503 | |
|
427 | <span onclick="return Rhodecode.comments.createComment(this)" | |
|
428 |
|
|
|
429 | style="${'display: none;' if comments and comments[-1].outdated else ''}"> | |
|
504 | % if comments and comments[-1].outdated: | |
|
505 | <span class="btn btn-secondary cb-comment-add-button comment-outdated}" | |
|
506 | style="display: none;}"> | |
|
430 | 507 | ${_('Add another comment')} |
|
431 | 508 | </span> |
|
509 | % else: | |
|
510 | <span onclick="return Rhodecode.comments.createComment(this)" | |
|
511 | class="btn btn-secondary cb-comment-add-button"> | |
|
512 | ${_('Add another comment')} | |
|
513 | </span> | |
|
514 | % endif | |
|
432 | 515 | |
|
433 | 516 | </div> |
|
434 | 517 | </%def> |
|
435 | 518 | |
|
436 | 519 | |
|
437 | 520 | <%def name="render_hunk_lines_sideside(hunk, use_comments=False)"> |
|
438 | 521 | %for i, line in enumerate(hunk.sideside): |
|
439 | 522 | <% |
|
440 | 523 | old_line_anchor, new_line_anchor = None, None |
|
441 | 524 | if line.original.lineno: |
|
442 | 525 | old_line_anchor = diff_line_anchor(hunk.filediff.source_file_path, line.original.lineno, 'o') |
|
443 | 526 | if line.modified.lineno: |
|
444 | 527 | new_line_anchor = diff_line_anchor(hunk.filediff.target_file_path, line.modified.lineno, 'n') |
|
445 | 528 | %> |
|
446 | 529 | |
|
447 | 530 | <tr class="cb-line"> |
|
448 | 531 | <td class="cb-data ${action_class(line.original.action)}" |
|
449 | 532 | data-line-number="${line.original.lineno}" |
|
450 | 533 | > |
|
451 | 534 | <div> |
|
452 | 535 | %if line.original.comments: |
|
453 | 536 | <i class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> |
|
454 | 537 | %endif |
|
455 | 538 | </div> |
|
456 | 539 | </td> |
|
457 | 540 | <td class="cb-lineno ${action_class(line.original.action)}" |
|
458 | 541 | data-line-number="${line.original.lineno}" |
|
459 | 542 | %if old_line_anchor: |
|
460 | 543 | id="${old_line_anchor}" |
|
461 | 544 | %endif |
|
462 | 545 | > |
|
463 | 546 | %if line.original.lineno: |
|
464 | 547 | <a name="${old_line_anchor}" href="#${old_line_anchor}">${line.original.lineno}</a> |
|
465 | 548 | %endif |
|
466 | 549 | </td> |
|
467 | 550 | <td class="cb-content ${action_class(line.original.action)}" |
|
468 | 551 | data-line-number="o${line.original.lineno}" |
|
469 | 552 | > |
|
470 | 553 | %if use_comments and line.original.lineno: |
|
471 | 554 | ${render_add_comment_button()} |
|
472 | 555 | %endif |
|
473 | 556 | <span class="cb-code">${line.original.action} ${line.original.content or '' | n}</span> |
|
474 | 557 | %if use_comments and line.original.lineno and line.original.comments: |
|
475 | 558 | ${inline_comments_container(line.original.comments)} |
|
476 | 559 | %endif |
|
477 | 560 | </td> |
|
478 | 561 | <td class="cb-data ${action_class(line.modified.action)}" |
|
479 | 562 | data-line-number="${line.modified.lineno}" |
|
480 | 563 | > |
|
481 | 564 | <div> |
|
482 | 565 | %if line.modified.comments: |
|
483 | 566 | <i class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> |
|
484 | 567 | %endif |
|
485 | 568 | </div> |
|
486 | 569 | </td> |
|
487 | 570 | <td class="cb-lineno ${action_class(line.modified.action)}" |
|
488 | 571 | data-line-number="${line.modified.lineno}" |
|
489 | 572 | %if new_line_anchor: |
|
490 | 573 | id="${new_line_anchor}" |
|
491 | 574 | %endif |
|
492 | 575 | > |
|
493 | 576 | %if line.modified.lineno: |
|
494 | 577 | <a name="${new_line_anchor}" href="#${new_line_anchor}">${line.modified.lineno}</a> |
|
495 | 578 | %endif |
|
496 | 579 | </td> |
|
497 | 580 | <td class="cb-content ${action_class(line.modified.action)}" |
|
498 | 581 | data-line-number="n${line.modified.lineno}" |
|
499 | 582 | > |
|
500 | 583 | %if use_comments and line.modified.lineno: |
|
501 | 584 | ${render_add_comment_button()} |
|
502 | 585 | %endif |
|
503 | 586 | <span class="cb-code">${line.modified.action} ${line.modified.content or '' | n}</span> |
|
504 | 587 | %if use_comments and line.modified.lineno and line.modified.comments: |
|
505 | 588 | ${inline_comments_container(line.modified.comments)} |
|
506 | 589 | %endif |
|
507 | 590 | </td> |
|
508 | 591 | </tr> |
|
509 | 592 | %endfor |
|
510 | 593 | </%def> |
|
511 | 594 | |
|
512 | 595 | |
|
513 | 596 | <%def name="render_hunk_lines_unified(hunk, use_comments=False)"> |
|
514 | 597 | %for old_line_no, new_line_no, action, content, comments in hunk.unified: |
|
515 | 598 | <% |
|
516 | 599 | old_line_anchor, new_line_anchor = None, None |
|
517 | 600 | if old_line_no: |
|
518 | 601 | old_line_anchor = diff_line_anchor(hunk.filediff.source_file_path, old_line_no, 'o') |
|
519 | 602 | if new_line_no: |
|
520 | 603 | new_line_anchor = diff_line_anchor(hunk.filediff.target_file_path, new_line_no, 'n') |
|
521 | 604 | %> |
|
522 | 605 | <tr class="cb-line"> |
|
523 | 606 | <td class="cb-data ${action_class(action)}"> |
|
524 | 607 | <div> |
|
525 | 608 | %if comments: |
|
526 | 609 | <i class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> |
|
527 | 610 | %endif |
|
528 | 611 | </div> |
|
529 | 612 | </td> |
|
530 | 613 | <td class="cb-lineno ${action_class(action)}" |
|
531 | 614 | data-line-number="${old_line_no}" |
|
532 | 615 | %if old_line_anchor: |
|
533 | 616 | id="${old_line_anchor}" |
|
534 | 617 | %endif |
|
535 | 618 | > |
|
536 | 619 | %if old_line_anchor: |
|
537 | 620 | <a name="${old_line_anchor}" href="#${old_line_anchor}">${old_line_no}</a> |
|
538 | 621 | %endif |
|
539 | 622 | </td> |
|
540 | 623 | <td class="cb-lineno ${action_class(action)}" |
|
541 | 624 | data-line-number="${new_line_no}" |
|
542 | 625 | %if new_line_anchor: |
|
543 | 626 | id="${new_line_anchor}" |
|
544 | 627 | %endif |
|
545 | 628 | > |
|
546 | 629 | %if new_line_anchor: |
|
547 | 630 | <a name="${new_line_anchor}" href="#${new_line_anchor}">${new_line_no}</a> |
|
548 | 631 | %endif |
|
549 | 632 | </td> |
|
550 | 633 | <td class="cb-content ${action_class(action)}" |
|
551 | 634 | data-line-number="${new_line_no and 'n' or 'o'}${new_line_no or old_line_no}" |
|
552 | 635 | > |
|
553 | 636 | %if use_comments: |
|
554 | 637 | ${render_add_comment_button()} |
|
555 | 638 | %endif |
|
556 | 639 | <span class="cb-code">${action} ${content or '' | n}</span> |
|
557 | 640 | %if use_comments and comments: |
|
558 | 641 | ${inline_comments_container(comments)} |
|
559 | 642 | %endif |
|
560 | 643 | </td> |
|
561 | 644 | </tr> |
|
562 | 645 | %endfor |
|
563 | 646 | </%def> |
|
564 | 647 | |
|
565 | 648 | <%def name="render_add_comment_button()"> |
|
566 | <button | |
|
567 | class="btn btn-small btn-primary cb-comment-box-opener" | |
|
568 | onclick="return Rhodecode.comments.createComment(this)" | |
|
569 | ><span>+</span></button> | |
|
649 | <button class="btn btn-small btn-primary cb-comment-box-opener" onclick="return Rhodecode.comments.createComment(this)"> | |
|
650 | <span><i class="icon-comment"></i></span> | |
|
651 | </button> | |
|
570 | 652 | </%def> |
|
571 | 653 | |
|
572 | 654 | <%def name="render_diffset_menu()"> |
|
573 | 655 | |
|
574 | 656 | <div class="diffset-menu clearinner"> |
|
575 | 657 | <div class="pull-right"> |
|
576 | 658 | <div class="btn-group"> |
|
577 | 659 | |
|
578 | 660 | <a |
|
579 | 661 | class="btn ${c.diffmode == 'sideside' and 'btn-primary'} tooltip" |
|
580 | 662 | title="${_('View side by side')}" |
|
581 | 663 | href="${h.url_replace(diffmode='sideside')}"> |
|
582 | 664 | <span>${_('Side by Side')}</span> |
|
583 | 665 | </a> |
|
584 | 666 | <a |
|
585 | 667 | class="btn ${c.diffmode == 'unified' and 'btn-primary'} tooltip" |
|
586 | 668 | title="${_('View unified')}" href="${h.url_replace(diffmode='unified')}"> |
|
587 | 669 | <span>${_('Unified')}</span> |
|
588 | 670 | </a> |
|
589 | 671 | </div> |
|
590 | 672 | </div> |
|
591 | 673 | |
|
592 | 674 | <div class="pull-left"> |
|
593 | 675 | <div class="btn-group"> |
|
594 | 676 | <a |
|
595 | 677 | class="btn" |
|
596 | 678 | href="#" |
|
597 | 679 | onclick="$('input[class=filediff-collapse-state]').prop('checked', false); return false">${_('Expand All Files')}</a> |
|
598 | 680 | <a |
|
599 | 681 | class="btn" |
|
600 | 682 | href="#" |
|
601 | 683 | onclick="$('input[class=filediff-collapse-state]').prop('checked', true); return false">${_('Collapse All Files')}</a> |
|
602 | 684 | <a |
|
603 | 685 | class="btn" |
|
604 | 686 | href="#" |
|
605 | 687 | onclick="return Rhodecode.comments.toggleWideMode(this)">${_('Wide Mode Diff')}</a> |
|
606 | 688 | </div> |
|
607 | 689 | </div> |
|
608 | 690 | </div> |
|
609 | 691 | </%def> |
@@ -1,113 +1,115 b'' | |||
|
1 | 1 | ## Changesets table ! |
|
2 | 2 | <%namespace name="base" file="/base/base.html"/> |
|
3 | 3 | |
|
4 | 4 | %if c.ancestor: |
|
5 | 5 | <div class="ancestor">${_('Common Ancestor Commit')}: |
|
6 | 6 | <a href="${h.url('changeset_home', |
|
7 | 7 | repo_name=c.repo_name, |
|
8 | 8 | revision=c.ancestor)}"> |
|
9 | 9 | ${h.short_id(c.ancestor)} |
|
10 | 10 | </a> |
|
11 | 11 | </div> |
|
12 | 12 | %endif |
|
13 | 13 | |
|
14 | 14 | <div class="container"> |
|
15 | 15 | <input type="hidden" name="__start__" value="revisions:sequence"> |
|
16 | 16 | <table class="rctable compare_view_commits"> |
|
17 | 17 | <tr> |
|
18 | 18 | <th>${_('Time')}</th> |
|
19 | 19 | <th>${_('Author')}</th> |
|
20 | 20 | <th>${_('Commit')}</th> |
|
21 | 21 | <th></th> |
|
22 | 22 | <th>${_('Description')}</th> |
|
23 | 23 | </tr> |
|
24 | 24 | %for commit in c.commit_ranges: |
|
25 | 25 | <tr id="row-${commit.raw_id}" |
|
26 | 26 | commit_id="${commit.raw_id}" |
|
27 | 27 | class="compare_select" |
|
28 | style="${'display: none' if c.collapse_all_commits else ''}" | |
|
28 | 29 | > |
|
29 | 30 | <td class="td-time"> |
|
30 | 31 | ${h.age_component(commit.date)} |
|
31 | 32 | </td> |
|
32 | 33 | <td class="td-user"> |
|
33 | 34 | ${base.gravatar_with_user(commit.author, 16)} |
|
34 | 35 | </td> |
|
35 | 36 | <td class="td-hash"> |
|
36 | 37 | <code> |
|
37 | 38 | <a href="${h.url('changeset_home', |
|
38 | 39 | repo_name=c.target_repo.repo_name, |
|
39 | 40 | revision=commit.raw_id)}"> |
|
40 | 41 | r${commit.revision}:${h.short_id(commit.raw_id)} |
|
41 | 42 | </a> |
|
42 | 43 | ${h.hidden('revisions',commit.raw_id)} |
|
43 | 44 | </code> |
|
44 | 45 | </td> |
|
45 | 46 | <td class="expand_commit" |
|
46 | 47 | data-commit-id="${commit.raw_id}" |
|
47 | 48 | title="${_( 'Expand commit message')}" |
|
48 | 49 | > |
|
49 | 50 | <div class="show_more_col"> |
|
50 | 51 | <i class="show_more"></i> |
|
51 | 52 | </div> |
|
52 | 53 | </td> |
|
53 | 54 | <td class="mid td-description"> |
|
54 | 55 | <div class="log-container truncate-wrap"> |
|
55 | 56 | <div |
|
56 | 57 | id="c-${commit.raw_id}" |
|
57 | 58 | class="message truncate" |
|
58 | 59 | data-message-raw="${commit.message}" |
|
59 | 60 | > |
|
60 | 61 | ${h.urlify_commit_message(commit.message, c.repo_name)} |
|
61 | 62 | </div> |
|
62 | 63 | </div> |
|
63 | 64 | </td> |
|
64 | 65 | </tr> |
|
65 | 66 | %endfor |
|
66 | <tr class="compare_select_hidden" style="display: none"> | |
|
67 | <tr class="compare_select_hidden" style="${'' if c.collapse_all_commits else 'display: none'}"> | |
|
67 | 68 | <td colspan="5"> |
|
68 | ${ungettext('%s commit hidden','%s commits hidden', len(c.commit_ranges)) % len(c.commit_ranges)} | |
|
69 | ${ungettext('%s commit hidden','%s commits hidden', len(c.commit_ranges)) % len(c.commit_ranges)}, | |
|
70 | <a href="#" onclick="$('.compare_select').show();$('.compare_select_hidden').hide(); return false">${ungettext('show it','show them', len(c.commit_ranges))}</a> | |
|
69 | 71 | </td> |
|
70 | 72 | </tr> |
|
71 | 73 | % if not c.commit_ranges: |
|
72 | 74 | <tr class="compare_select"> |
|
73 | 75 | <td colspan="5"> |
|
74 | 76 | ${_('No commits in this compare')} |
|
75 | 77 | </td> |
|
76 | 78 | </tr> |
|
77 | 79 | % endif |
|
78 | 80 | </table> |
|
79 | 81 | <input type="hidden" name="__end__" value="revisions:sequence"> |
|
80 | 82 | |
|
81 | 83 | </div> |
|
82 | 84 | |
|
83 | 85 | <script> |
|
84 | 86 | $('.expand_commit').on('click',function(e){ |
|
85 | 87 | var target_expand = $(this); |
|
86 | 88 | var cid = target_expand.data('commitId'); |
|
87 | 89 | |
|
88 | 90 | // ## TODO: dan: extract styles into css, and just toggleClass('open') here |
|
89 | 91 | if (target_expand.hasClass('open')){ |
|
90 | 92 | $('#c-'+cid).css({ |
|
91 | 93 | 'height': '1.5em', |
|
92 | 94 | 'white-space': 'nowrap', |
|
93 | 95 | 'text-overflow': 'ellipsis', |
|
94 | 96 | 'overflow':'hidden' |
|
95 | 97 | }); |
|
96 | 98 | target_expand.removeClass('open'); |
|
97 | 99 | } |
|
98 | 100 | else { |
|
99 | 101 | $('#c-'+cid).css({ |
|
100 | 102 | 'height': 'auto', |
|
101 | 103 | 'white-space': 'pre-line', |
|
102 | 104 | 'text-overflow': 'initial', |
|
103 | 105 | 'overflow':'visible' |
|
104 | 106 | }); |
|
105 | 107 | target_expand.addClass('open'); |
|
106 | 108 | } |
|
107 | 109 | }); |
|
108 | 110 | |
|
109 | 111 | $('.compare_select').on('click',function(e){ |
|
110 | 112 | var cid = $(this).attr('commit_id'); |
|
111 | 113 | $('#row-'+cid).toggleClass('hl', !$('#row-'+cid).hasClass('hl')); |
|
112 | 114 | }); |
|
113 | 115 | </script> |
@@ -1,317 +1,317 b'' | |||
|
1 | 1 | ## DATA TABLE RE USABLE ELEMENTS |
|
2 | 2 | ## usage: |
|
3 | 3 | ## <%namespace name="dt" file="/data_table/_dt_elements.html"/> |
|
4 | 4 | <%namespace name="base" file="/base/base.html"/> |
|
5 | 5 | |
|
6 | 6 | ## REPOSITORY RENDERERS |
|
7 | 7 | <%def name="quick_menu(repo_name)"> |
|
8 | 8 | <i class="pointer icon-more"></i> |
|
9 | 9 | <div class="menu_items_container hidden"> |
|
10 | 10 | <ul class="menu_items"> |
|
11 | 11 | <li> |
|
12 | 12 | <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=repo_name)}"> |
|
13 | 13 | <span>${_('Summary')}</span> |
|
14 | 14 | </a> |
|
15 | 15 | </li> |
|
16 | 16 | <li> |
|
17 | 17 | <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=repo_name)}"> |
|
18 | 18 | <span>${_('Changelog')}</span> |
|
19 | 19 | </a> |
|
20 | 20 | </li> |
|
21 | 21 | <li> |
|
22 | 22 | <a title="${_('Files')}" href="${h.url('files_home',repo_name=repo_name)}"> |
|
23 | 23 | <span>${_('Files')}</span> |
|
24 | 24 | </a> |
|
25 | 25 | </li> |
|
26 | 26 | <li> |
|
27 | 27 | <a title="${_('Fork')}" href="${h.url('repo_fork_home',repo_name=repo_name)}"> |
|
28 | 28 | <span>${_('Fork')}</span> |
|
29 | 29 | </a> |
|
30 | 30 | </li> |
|
31 | 31 | </ul> |
|
32 | 32 | </div> |
|
33 | 33 | </%def> |
|
34 | 34 | |
|
35 | 35 | <%def name="repo_name(name,rtype,rstate,private,fork_of,short_name=False,admin=False)"> |
|
36 | 36 | <% |
|
37 | 37 | def get_name(name,short_name=short_name): |
|
38 | 38 | if short_name: |
|
39 | 39 | return name.split('/')[-1] |
|
40 | 40 | else: |
|
41 | 41 | return name |
|
42 | 42 | %> |
|
43 | 43 | <div class="${'repo_state_pending' if rstate == 'repo_state_pending' else ''} truncate"> |
|
44 | 44 | ##NAME |
|
45 | 45 | <a href="${h.url('edit_repo' if admin else 'summary_home',repo_name=name)}"> |
|
46 | 46 | |
|
47 | 47 | ##TYPE OF REPO |
|
48 | 48 | %if h.is_hg(rtype): |
|
49 | 49 | <span title="${_('Mercurial repository')}"><i class="icon-hg"></i></span> |
|
50 | 50 | %elif h.is_git(rtype): |
|
51 | 51 | <span title="${_('Git repository')}"><i class="icon-git"></i></span> |
|
52 | 52 | %elif h.is_svn(rtype): |
|
53 | 53 | <span title="${_('Subversion repository')}"><i class="icon-svn"></i></span> |
|
54 | 54 | %endif |
|
55 | 55 | |
|
56 | 56 | ##PRIVATE/PUBLIC |
|
57 | 57 | %if private and c.visual.show_private_icon: |
|
58 | 58 | <i class="icon-lock" title="${_('Private repository')}"></i> |
|
59 | 59 | %elif not private and c.visual.show_public_icon: |
|
60 | 60 | <i class="icon-unlock-alt" title="${_('Public repository')}"></i> |
|
61 | 61 | %else: |
|
62 | 62 | <span></span> |
|
63 | 63 | %endif |
|
64 | 64 | ${get_name(name)} |
|
65 | 65 | </a> |
|
66 | 66 | %if fork_of: |
|
67 | 67 | <a href="${h.url('summary_home',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a> |
|
68 | 68 | %endif |
|
69 | 69 | %if rstate == 'repo_state_pending': |
|
70 | 70 | <i class="icon-cogs" title="${_('Repository creating in progress...')}"></i> |
|
71 | 71 | %endif |
|
72 | 72 | </div> |
|
73 | 73 | </%def> |
|
74 | 74 | |
|
75 | 75 | <%def name="repo_desc(description)"> |
|
76 | 76 | <div class="truncate-wrap">${description}</div> |
|
77 | 77 | </%def> |
|
78 | 78 | |
|
79 | 79 | <%def name="last_change(last_change)"> |
|
80 | 80 | ${h.age_component(last_change)} |
|
81 | 81 | </%def> |
|
82 | 82 | |
|
83 | 83 | <%def name="revision(name,rev,tip,author,last_msg)"> |
|
84 | 84 | <div> |
|
85 | 85 | %if rev >= 0: |
|
86 | 86 | <code><a title="${h.tooltip('%s:\n\n%s' % (author,last_msg))}" class="tooltip" href="${h.url('changeset_home',repo_name=name,revision=tip)}">${'r%s:%s' % (rev,h.short_id(tip))}</a></code> |
|
87 | 87 | %else: |
|
88 | 88 | ${_('No commits yet')} |
|
89 | 89 | %endif |
|
90 | 90 | </div> |
|
91 | 91 | </%def> |
|
92 | 92 | |
|
93 | 93 | <%def name="rss(name)"> |
|
94 | 94 | %if c.rhodecode_user.username != h.DEFAULT_USER: |
|
95 | 95 | <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name,auth_token=c.rhodecode_user.feed_token)}"><i class="icon-rss-sign"></i></a> |
|
96 | 96 | %else: |
|
97 | 97 | <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name)}"><i class="icon-rss-sign"></i></a> |
|
98 | 98 | %endif |
|
99 | 99 | </%def> |
|
100 | 100 | |
|
101 | 101 | <%def name="atom(name)"> |
|
102 | 102 | %if c.rhodecode_user.username != h.DEFAULT_USER: |
|
103 | 103 | <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name,auth_token=c.rhodecode_user.feed_token)}"><i class="icon-rss-sign"></i></a> |
|
104 | 104 | %else: |
|
105 | 105 | <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name)}"><i class="icon-rss-sign"></i></a> |
|
106 | 106 | %endif |
|
107 | 107 | </%def> |
|
108 | 108 | |
|
109 | 109 | <%def name="user_gravatar(email, size=16)"> |
|
110 | 110 | <div class="rc-user tooltip" title="${h.author_string(email)}"> |
|
111 | 111 | ${base.gravatar(email, 16)} |
|
112 | 112 | </div> |
|
113 | 113 | </%def> |
|
114 | 114 | |
|
115 | 115 | <%def name="repo_actions(repo_name, super_user=True)"> |
|
116 | 116 | <div> |
|
117 | 117 | <div class="grid_edit"> |
|
118 | 118 | <a href="${h.url('edit_repo',repo_name=repo_name)}" title="${_('Edit')}"> |
|
119 | 119 | <i class="icon-pencil"></i>Edit</a> |
|
120 | 120 | </div> |
|
121 | 121 | <div class="grid_delete"> |
|
122 | 122 | ${h.secure_form(h.url('repo', repo_name=repo_name),method='delete')} |
|
123 | 123 | ${h.submit('remove_%s' % repo_name,_('Delete'),class_="btn btn-link btn-danger", |
|
124 | 124 | onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")} |
|
125 | 125 | ${h.end_form()} |
|
126 | 126 | </div> |
|
127 | 127 | </div> |
|
128 | 128 | </%def> |
|
129 | 129 | |
|
130 | 130 | <%def name="repo_state(repo_state)"> |
|
131 | 131 | <div> |
|
132 | 132 | %if repo_state == 'repo_state_pending': |
|
133 | 133 | <div class="tag tag4">${_('Creating')}</div> |
|
134 | 134 | %elif repo_state == 'repo_state_created': |
|
135 | 135 | <div class="tag tag1">${_('Created')}</div> |
|
136 | 136 | %else: |
|
137 | 137 | <div class="tag alert2" title="${repo_state}">invalid</div> |
|
138 | 138 | %endif |
|
139 | 139 | </div> |
|
140 | 140 | </%def> |
|
141 | 141 | |
|
142 | 142 | |
|
143 | 143 | ## REPO GROUP RENDERERS |
|
144 | 144 | <%def name="quick_repo_group_menu(repo_group_name)"> |
|
145 | 145 | <i class="pointer icon-more"></i> |
|
146 | 146 | <div class="menu_items_container hidden"> |
|
147 | 147 | <ul class="menu_items"> |
|
148 | 148 | <li> |
|
149 | 149 | <a href="${h.url('repo_group_home',group_name=repo_group_name)}"> |
|
150 | 150 | <span class="icon"> |
|
151 | 151 | <i class="icon-file-text"></i> |
|
152 | 152 | </span> |
|
153 | 153 | <span>${_('Summary')}</span> |
|
154 | 154 | </a> |
|
155 | 155 | </li> |
|
156 | 156 | |
|
157 | 157 | </ul> |
|
158 | 158 | </div> |
|
159 | 159 | </%def> |
|
160 | 160 | |
|
161 | 161 | <%def name="repo_group_name(repo_group_name, children_groups=None)"> |
|
162 | 162 | <div> |
|
163 | 163 | <a href="${h.url('repo_group_home',group_name=repo_group_name)}"> |
|
164 | 164 | <i class="icon-folder-close" title="${_('Repository group')}"></i> |
|
165 | 165 | %if children_groups: |
|
166 | 166 | ${h.literal(' » '.join(children_groups))} |
|
167 | 167 | %else: |
|
168 | 168 | ${repo_group_name} |
|
169 | 169 | %endif |
|
170 | 170 | </a> |
|
171 | 171 | </div> |
|
172 | 172 | </%def> |
|
173 | 173 | |
|
174 | 174 | <%def name="repo_group_desc(description)"> |
|
175 | 175 | <div class="truncate-wrap">${description}</div> |
|
176 | 176 | </%def> |
|
177 | 177 | |
|
178 | 178 | <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)"> |
|
179 | 179 | <div class="grid_edit"> |
|
180 | 180 | <a href="${h.url('edit_repo_group',group_name=repo_group_name)}" title="${_('Edit')}">Edit</a> |
|
181 | 181 | </div> |
|
182 | 182 | <div class="grid_delete"> |
|
183 | 183 | ${h.secure_form(h.url('delete_repo_group', group_name=repo_group_name),method='delete')} |
|
184 | 184 | ${h.submit('remove_%s' % repo_group_name,_('Delete'),class_="btn btn-link btn-danger", |
|
185 | 185 | onclick="return confirm('"+ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)+"');")} |
|
186 | 186 | ${h.end_form()} |
|
187 | 187 | </div> |
|
188 | 188 | </%def> |
|
189 | 189 | |
|
190 | 190 | |
|
191 | 191 | <%def name="user_actions(user_id, username)"> |
|
192 | 192 | <div class="grid_edit"> |
|
193 | 193 | <a href="${h.url('edit_user',user_id=user_id)}" title="${_('Edit')}"> |
|
194 | 194 | <i class="icon-pencil"></i>Edit</a> |
|
195 | 195 | </div> |
|
196 | 196 | <div class="grid_delete"> |
|
197 | 197 | ${h.secure_form(h.url('delete_user', user_id=user_id),method='delete')} |
|
198 | 198 | ${h.submit('remove_',_('Delete'),id="remove_user_%s" % user_id, class_="btn btn-link btn-danger", |
|
199 | 199 | onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")} |
|
200 | 200 | ${h.end_form()} |
|
201 | 201 | </div> |
|
202 | 202 | </%def> |
|
203 | 203 | |
|
204 | 204 | <%def name="user_group_actions(user_group_id, user_group_name)"> |
|
205 | 205 | <div class="grid_edit"> |
|
206 | 206 | <a href="${h.url('edit_users_group', user_group_id=user_group_id)}" title="${_('Edit')}">Edit</a> |
|
207 | 207 | </div> |
|
208 | 208 | <div class="grid_delete"> |
|
209 | 209 | ${h.secure_form(h.url('delete_users_group', user_group_id=user_group_id),method='delete')} |
|
210 | 210 | ${h.submit('remove_',_('Delete'),id="remove_group_%s" % user_group_id, class_="btn btn-link btn-danger", |
|
211 | 211 | onclick="return confirm('"+_('Confirm to delete this user group: %s') % user_group_name+"');")} |
|
212 | 212 | ${h.end_form()} |
|
213 | 213 | </div> |
|
214 | 214 | </%def> |
|
215 | 215 | |
|
216 | 216 | |
|
217 | 217 | <%def name="user_name(user_id, username)"> |
|
218 | 218 | ${h.link_to(h.person(username, 'username_or_name_or_email'), h.url('edit_user', user_id=user_id))} |
|
219 | 219 | </%def> |
|
220 | 220 | |
|
221 | 221 | <%def name="user_profile(username)"> |
|
222 | 222 | ${base.gravatar_with_user(username, 16)} |
|
223 | 223 | </%def> |
|
224 | 224 | |
|
225 | 225 | <%def name="user_group_name(user_group_id, user_group_name)"> |
|
226 | 226 | <div> |
|
227 | 227 | <a href="${h.url('edit_users_group', user_group_id=user_group_id)}"> |
|
228 | 228 | <i class="icon-group" title="${_('User group')}"></i> ${user_group_name}</a> |
|
229 | 229 | </div> |
|
230 | 230 | </%def> |
|
231 | 231 | |
|
232 | 232 | |
|
233 | 233 | ## GISTS |
|
234 | 234 | |
|
235 | 235 | <%def name="gist_gravatar(full_contact)"> |
|
236 | 236 | <div class="gist_gravatar"> |
|
237 | 237 | ${base.gravatar(full_contact, 30)} |
|
238 | 238 | </div> |
|
239 | 239 | </%def> |
|
240 | 240 | |
|
241 | 241 | <%def name="gist_access_id(gist_access_id, full_contact)"> |
|
242 | 242 | <div> |
|
243 | 243 | <b> |
|
244 | 244 | <a href="${h.url('gist',gist_id=gist_access_id)}">gist: ${gist_access_id}</a> |
|
245 | 245 | </b> |
|
246 | 246 | </div> |
|
247 | 247 | </%def> |
|
248 | 248 | |
|
249 | 249 | <%def name="gist_author(full_contact, created_on, expires)"> |
|
250 | 250 | ${base.gravatar_with_user(full_contact, 16)} |
|
251 | 251 | </%def> |
|
252 | 252 | |
|
253 | 253 | |
|
254 | 254 | <%def name="gist_created(created_on)"> |
|
255 | 255 | <div class="created"> |
|
256 | 256 | ${h.age_component(created_on, time_is_local=True)} |
|
257 | 257 | </div> |
|
258 | 258 | </%def> |
|
259 | 259 | |
|
260 | 260 | <%def name="gist_expires(expires)"> |
|
261 | 261 | <div class="created"> |
|
262 | 262 | %if expires == -1: |
|
263 | 263 | ${_('never')} |
|
264 | 264 | %else: |
|
265 | 265 | ${h.age_component(h.time_to_utcdatetime(expires))} |
|
266 | 266 | %endif |
|
267 | 267 | </div> |
|
268 | 268 | </%def> |
|
269 | 269 | |
|
270 | 270 | <%def name="gist_type(gist_type)"> |
|
271 | 271 | %if gist_type != 'public': |
|
272 | 272 | <div class="tag">${_('Private')}</div> |
|
273 | 273 | %endif |
|
274 | 274 | </%def> |
|
275 | 275 | |
|
276 | 276 | <%def name="gist_description(gist_description)"> |
|
277 | 277 | ${gist_description} |
|
278 | 278 | </%def> |
|
279 | 279 | |
|
280 | 280 | |
|
281 | 281 | ## PULL REQUESTS GRID RENDERERS |
|
282 | 282 | |
|
283 | 283 | <%def name="pullrequest_target_repo(repo_name)"> |
|
284 | 284 | <div class="truncate"> |
|
285 | 285 | ${h.link_to(repo_name,h.url('summary_home',repo_name=repo_name))} |
|
286 | 286 | </div> |
|
287 | 287 | </%def> |
|
288 | 288 | <%def name="pullrequest_status(status)"> |
|
289 | 289 | <div class="${'flag_status %s' % status} pull-left"></div> |
|
290 | 290 | </%def> |
|
291 | 291 | |
|
292 | 292 | <%def name="pullrequest_title(title, description)"> |
|
293 | 293 | ${title} <br/> |
|
294 | 294 | ${h.shorter(description, 40)} |
|
295 | 295 | </%def> |
|
296 | 296 | |
|
297 | 297 | <%def name="pullrequest_comments(comments_nr)"> |
|
298 |
<i class="icon-comment |
|
|
298 | <i class="icon-comment"></i> ${comments_nr} | |
|
299 | 299 | </%def> |
|
300 | 300 | |
|
301 | 301 | <%def name="pullrequest_name(pull_request_id, target_repo_name, short=False)"> |
|
302 | 302 | <a href="${h.url('pullrequest_show',repo_name=target_repo_name,pull_request_id=pull_request_id)}"> |
|
303 | 303 | % if short: |
|
304 | 304 | #${pull_request_id} |
|
305 | 305 | % else: |
|
306 | 306 | ${_('Pull request #%(pr_number)s') % {'pr_number': pull_request_id,}} |
|
307 | 307 | % endif |
|
308 | 308 | </a> |
|
309 | 309 | </%def> |
|
310 | 310 | |
|
311 | 311 | <%def name="pullrequest_updated_on(updated_on)"> |
|
312 | 312 | ${h.age_component(h.time_to_utcdatetime(updated_on))} |
|
313 | 313 | </%def> |
|
314 | 314 | |
|
315 | 315 | <%def name="pullrequest_author(full_contact)"> |
|
316 | 316 | ${base.gravatar_with_user(full_contact, 16)} |
|
317 | 317 | </%def> |
@@ -1,545 +1,545 b'' | |||
|
1 | 1 | ## -*- coding: utf-8 -*- |
|
2 | 2 | <%inherit file="/debug_style/index.html"/> |
|
3 | 3 | |
|
4 | 4 | <%def name="breadcrumbs_links()"> |
|
5 | 5 | ${h.link_to(_('Style'), h.url('debug_style_home'))} |
|
6 | 6 | » |
|
7 | 7 | ${c.active} |
|
8 | 8 | </%def> |
|
9 | 9 | |
|
10 | 10 | |
|
11 | 11 | <%def name="real_main()"> |
|
12 | 12 | <div class="box"> |
|
13 | 13 | <div class="title"> |
|
14 | 14 | ${self.breadcrumbs()} |
|
15 | 15 | </div> |
|
16 | 16 | |
|
17 | 17 | <div class='sidebar-col-wrapper'> |
|
18 | 18 | ##main |
|
19 | 19 | ${self.sidebar()} |
|
20 | 20 | |
|
21 | 21 | <div class="main-content"> |
|
22 | 22 | |
|
23 | 23 | <div style="opacity:.5"> |
|
24 | 24 | |
|
25 | 25 | <h2>Simple tables</h2> |
|
26 | 26 | |
|
27 | 27 | <p>These styles will be adjusted later to provide a baseline style |
|
28 | 28 | for all tables without classes added, whether part of the |
|
29 | 29 | application or not. Currently, some of the |
|
30 | 30 | application-specific styles are applied to this table.</p> |
|
31 | 31 | <p>This is a baseline style for all tables, whether part of the |
|
32 | 32 | application or not. It has no class applied for styling. Use |
|
33 | 33 | the "rctable" class as outlined before for tables which are |
|
34 | 34 | part of the RhodeCode application.</p> |
|
35 | 35 | <table> |
|
36 | 36 | <tbody> |
|
37 | 37 | <tr> |
|
38 | 38 | <th>Header A</th> |
|
39 | 39 | <th>Header B</th> |
|
40 | 40 | <th>Header C</th> |
|
41 | 41 | <th>Header D</th> |
|
42 | 42 | </tr> |
|
43 | 43 | <tr> |
|
44 | 44 | <td>Content of col A</td> |
|
45 | 45 | <td>Content of col B</td> |
|
46 | 46 | <td>Content of col C</td> |
|
47 | 47 | <td>Content of col D</td> |
|
48 | 48 | </tr> |
|
49 | 49 | <tr> |
|
50 | 50 | <td>Content of col A</td> |
|
51 | 51 | <td>Content of col B</td> |
|
52 | 52 | <td>Content of col C</td> |
|
53 | 53 | <td>Content of col D</td> |
|
54 | 54 | </tr> |
|
55 | 55 | <tr> |
|
56 | 56 | <td>Content of col A</td> |
|
57 | 57 | <td>Content of col B</td> |
|
58 | 58 | <td>Content of col C</td> |
|
59 | 59 | <td>Content of col D</td> |
|
60 | 60 | </tr> |
|
61 | 61 | <tr> |
|
62 | 62 | <td>Content of col A</td> |
|
63 | 63 | <td>Content of col B</td> |
|
64 | 64 | <td>Content of col C</td> |
|
65 | 65 | <td>Content of col D</td> |
|
66 | 66 | </tr> |
|
67 | 67 | </tbody> |
|
68 | 68 | </table> |
|
69 | 69 | </div> |
|
70 | 70 | |
|
71 | 71 | |
|
72 | 72 | |
|
73 | 73 | |
|
74 | 74 | <h2>RC application table with examples</h2> |
|
75 | 75 | |
|
76 | 76 | <p>This is a standard table which applies the rhodecode-specific styling to be used |
|
77 | 77 | throughout the application; it has <code><table class="rctable"></code>. |
|
78 | 78 | <br/> |
|
79 | 79 | By default, table data is not truncated, and wraps inside of the <code><td> |
|
80 | 80 | ;</code>. To prevent wrapping and contain data on one line, use the <code>< |
|
81 | 81 | class="truncate-wrap"></code> on the <code><td></code>, and <code>span |
|
82 | 82 | class="truncate"</code> around the specific data to be truncated. |
|
83 | 83 | </p> |
|
84 | 84 | <p> |
|
85 | 85 | Ellipsis is added via CSS. Please always add a row of headers using <code><th |
|
86 | 86 | ></code> to the top of a table. |
|
87 | 87 | </p> |
|
88 | 88 | |
|
89 | 89 | ## TODO: johbo: in case we have more tables with examples, we should |
|
90 | 90 | ## create a generic class here. |
|
91 | 91 | <table class="rctable issuetracker"> |
|
92 | 92 | <thead> |
|
93 | 93 | <tr> |
|
94 | 94 | <th>Header A</th> |
|
95 | 95 | <th>Header B</th> |
|
96 | 96 | <th>Header C</th> |
|
97 | 97 | <th>Header D</th> |
|
98 | 98 | </tr> |
|
99 | 99 | </thead> |
|
100 | 100 | <tbody> |
|
101 | 101 | <tr> |
|
102 | 102 | <td class="issue-tracker-example"> |
|
103 | 103 | Example of col A |
|
104 | 104 | </td> |
|
105 | 105 | <td class="issue-tracker-example"> |
|
106 | 106 | Example of col B |
|
107 | 107 | </td> |
|
108 | 108 | <td class="issue-tracker-example"> |
|
109 | 109 | Example of col C |
|
110 | 110 | </td> |
|
111 | 111 | <td class="issue-tracker-example"> |
|
112 | 112 | Example of col D |
|
113 | 113 | </td> |
|
114 | 114 | </tr> |
|
115 | 115 | <tr> |
|
116 | 116 | <td>Content of col A</td> |
|
117 | 117 | <td>Content of col B</td> |
|
118 | 118 | <td>Content of col C which is very long and will not be |
|
119 | 119 | truncated because sometimes people just want to write |
|
120 | 120 | really, really long commit messages which explain what |
|
121 | 121 | they did in excruciating detail and you really, really |
|
122 | 122 | want to read them.</td> |
|
123 | 123 | <td>Content of col D</td> |
|
124 | 124 | </tr> |
|
125 | 125 | <tr> |
|
126 | 126 | <td>Content of col A</td> |
|
127 | 127 | <td>Content of col B</td> |
|
128 | 128 | <td>Content of col C</td> |
|
129 | 129 | <td class="truncate-wrap"><span class="truncate">Truncated |
|
130 | 130 | content of column D truncate truncate truncatetruncate |
|
131 | 131 | truncate truncate</span></td> |
|
132 | 132 | </tr> |
|
133 | 133 | </tbody> |
|
134 | 134 | </table> |
|
135 | 135 | |
|
136 | 136 | <h2>RC application table data classes</h2> |
|
137 | 137 | |
|
138 | 138 | <p>The following tables contain documentation of all existing table data classes. |
|
139 | 139 | Please update when new classes are made. |
|
140 | 140 | </p> |
|
141 | 141 | <table class="rctable examples"> |
|
142 | 142 | <thead> |
|
143 | 143 | <tr> |
|
144 | 144 | <th>Class</th> |
|
145 | 145 | <th>Description</th> |
|
146 | 146 | <th>Example</th> |
|
147 | 147 | </tr> |
|
148 | 148 | </thead> |
|
149 | 149 | <tbody> |
|
150 | 150 | <td>td-user</td> |
|
151 | 151 | <td>Any username/gravatar combination (see also Icons style).</td> |
|
152 | 152 | <td class="td-user author"> |
|
153 | 153 | <img class="gravatar" alt="gravatar" src="https://secure.gravatar.com/avatar/0c9a7e6674b6f0b35d98dbe073e3f0ab?d=identicon&s=32" height="16" width="16"> |
|
154 | 154 | <span title="Oliver Strobel <oliver@rhodecode.com>" class="user">ostrobel (Oliver Strobel)</span> |
|
155 | 155 | </td> |
|
156 | 156 | </tr> |
|
157 | 157 | <tr> |
|
158 | 158 | <td>td-hash</td> |
|
159 | 159 | <td>Any hash; a commit, revision, etc. Use <code><pre></code> and header 'Commit'</td> |
|
160 | 160 | <td class="td-commit"> |
|
161 | 161 | <pre><a href="/anothercpythonforkkkk/files/8d6b27837c6979983b037693fe975cdbb761b500/">r93699:8d6b27837c69</a></pre> |
|
162 | 162 | </td> |
|
163 | 163 | </tr> |
|
164 | 164 | <tr> |
|
165 | 165 | <td>td-rss</td> |
|
166 | 166 | <td>RSS feed link icon</td> |
|
167 | 167 | <td class="td-rss"> |
|
168 | 168 | <a title="Subscribe to rss feed" href="/feed/rss"><i class="icon-rss-sign"></i></a> |
|
169 | 169 | </td> |
|
170 | 170 | </tr> |
|
171 | 171 | <tr> |
|
172 | 172 | <td>td-componentname</td> |
|
173 | 173 | <td>Any group, file, gist, or directory name.</td> |
|
174 | 174 | <td class="td-componentname"> |
|
175 | 175 | <a href="/cpythonfork"> |
|
176 | 176 | <span title="Mercurial repository"><i class="icon-hg"></i></span> |
|
177 | 177 | <i class="icon-unlock-alt" title="Public repository"></i> |
|
178 | 178 | rhodecode-dev-restyle-fork |
|
179 | 179 | </a> |
|
180 | 180 | </td> |
|
181 | 181 | </tr> |
|
182 | 182 | <tr> |
|
183 | 183 | <td>td-tags</td> |
|
184 | 184 | <td>Any cell containing tags, including branches and bookmarks.</td> |
|
185 | 185 | <td class="td-tags"> |
|
186 | 186 | <span class="branchtag tag" title="Branch default"> |
|
187 | 187 | <a href="/rhodecode-dev-restyle- fork/changelog?branch=default"><i class="icon-code-fork"></i>default</a> |
|
188 | 188 | </span> |
|
189 | 189 | </td> |
|
190 | 190 | </tr> |
|
191 | 191 | <tr> |
|
192 | 192 | <td>tags-truncate</td> |
|
193 | 193 | <td>Used to truncate a cell containing tags; avoid if possible.</td> |
|
194 | 194 | <td class="td-tags truncate-wrap"> |
|
195 | 195 | <div class="truncate tags-truncate"> |
|
196 | 196 | <div class="autoexpand"> |
|
197 | 197 | <span class="tagtag tag" title="Tag tip"> |
|
198 | 198 | <a href="/rhodecode-dev-restyle-fork/files/e519d5a0e71466d27257ddff921c4a13c540408e/"><i class="icon-tag"></i>tip</a> |
|
199 | 199 | </span> |
|
200 | 200 | <span class="branchtag tag" title="Branch default"> |
|
201 | 201 | <a href="/rhodecode-dev-restyle-fork/changelog?branch=default"><i class="icon-code-fork"></i>default</a> |
|
202 | 202 | </span> |
|
203 | 203 | <span class="branchtag tag" title="Branch default"> |
|
204 | 204 | <a href="/rhodecode-dev-restyle-fork/changelog?branch=default"><i class="icon-code-fork"></i>default</a> |
|
205 | 205 | </span> |
|
206 | 206 | </div> |
|
207 | 207 | </div> |
|
208 | 208 | </td> |
|
209 | 209 | </tr> |
|
210 | 210 | <tr> |
|
211 | 211 | <td>td-ip</td> |
|
212 | 212 | <td>Any ip address.</td> |
|
213 | 213 | <td class="td-ip"> |
|
214 | 214 | 172.16.115.168 |
|
215 | 215 | </td> |
|
216 | 216 | </tr> |
|
217 | 217 | <tr> |
|
218 | 218 | <td>td-type</td> |
|
219 | 219 | <td>A state or an auth type.</td> |
|
220 | 220 | <td class="td-type"> |
|
221 | 221 | rhodecode |
|
222 | 222 | </td> |
|
223 | 223 | </tr> |
|
224 | 224 | <tr> |
|
225 | 225 | <td>td-authtoken</td> |
|
226 | 226 | <td>For auth tokens. Use truncate classes for hover expand; see html.</td> |
|
227 | 227 | <td class="truncate-wrap td-authtoken"> |
|
228 | 228 | <div class="truncate autoexpand"> |
|
229 | 229 | <code>688df65b87d3ad16ae9f8fc6338a551d40f41c7a</code> |
|
230 | 230 | </div> |
|
231 | 231 | </td> |
|
232 | 232 | </tr> |
|
233 | 233 | <tr> |
|
234 | 234 | <td>td-action</td> |
|
235 | 235 | <td>Buttons which perform an action.</td> |
|
236 | 236 | <td class="td-action"> |
|
237 | 237 | <div class="grid_edit"> |
|
238 | 238 | <a href="/_admin/users/2/edit" title="edit"> |
|
239 | 239 | <i class="icon-pencil"></i>Edit</a> |
|
240 | 240 | </div> |
|
241 | 241 | <div class="grid_delete"> |
|
242 | 242 | <form action="/_admin/users/2" method="post"> |
|
243 | 243 | <i class="icon-remove-sign"></i> |
|
244 | 244 | <input class="btn btn-danger btn-link" id="remove_user_2" name="remove_" type="submit" value="delete"> |
|
245 | 245 | </form> |
|
246 | 246 | </div> |
|
247 | 247 | </td> |
|
248 | 248 | </tr> |
|
249 | 249 | <tr> |
|
250 | 250 | <td>td-radio</td> |
|
251 | 251 | <td>Radio buttons for a form. Centers element.</td> |
|
252 | 252 | <td class="td-radio"> |
|
253 | 253 | <input type="radio" checked="checked" value="" name="1" id="read"></td> |
|
254 | 254 | </tr> |
|
255 | 255 | <tr> |
|
256 | 256 | <td>td-checkbox</td> |
|
257 | 257 | <td>Checkbox for a form. Centers element.</td> |
|
258 | 258 | <td class="td-checkbox"> |
|
259 | 259 | <input type="checkbox" checked="checked" value="" name="1" id="read"></td> |
|
260 | 260 | </tr> |
|
261 | 261 | <tr> |
|
262 | 262 | <tr> |
|
263 | 263 | <td>td-buttons</td> |
|
264 | 264 | <td>Buttons.</td> |
|
265 | 265 | <td class="td-buttons"> |
|
266 | 266 | <span class="btn btn-mini btn-primary">feed access</span> |
|
267 | 267 | </td> |
|
268 | 268 | </tr> |
|
269 | 269 | <tr> |
|
270 | 270 | <td>td-compare</td> |
|
271 | 271 | <td>Radio buttons to compare commits.</td> |
|
272 | 272 | <td class=" td-compare"> |
|
273 | 273 | <input class="compare-radio-button" type="radio" name="compare_source" value="2.0"> |
|
274 | 274 | <input class="compare-radio-button" type="radio" name="compare_target" value="2.0"> |
|
275 | 275 | </td> |
|
276 | 276 | </tr> |
|
277 | 277 | <tr> |
|
278 | 278 | <td>td-comments</td> |
|
279 | 279 | <td>Comments indicator icon.</td> |
|
280 | 280 | <td> |
|
281 |
<i class="icon-comment |
|
|
281 | <i class="icon-comment"></i> 0 | |
|
282 | 282 | </td> |
|
283 | 283 | </tr> |
|
284 | 284 | <tr> |
|
285 | 285 | <td>td-status</td> |
|
286 | 286 | <td>Status indicator icon.</td> |
|
287 | 287 | <td class="td-description"> |
|
288 | 288 | <div class="flag_status under_review pull-left"></div> |
|
289 | 289 | </td> |
|
290 | 290 | </tr> |
|
291 | 291 | </tbody> |
|
292 | 292 | </table> |
|
293 | 293 | <table class="dataTable rctable examples"> |
|
294 | 294 | <tbody> |
|
295 | 295 | <tr> |
|
296 | 296 | <td>quick_repo_menu</td> |
|
297 | 297 | <td>Hidden menu generated by dataTable.</td> |
|
298 | 298 | <td class="quick_repo_menu"> |
|
299 | 299 | <i class="pointer icon-more"></i> |
|
300 | 300 | <div class="menu_items_container" style="display: none;"> |
|
301 | 301 | <ul class="menu_items"> |
|
302 | 302 | <li> |
|
303 | 303 | <a title="Summary" href="/anothercpythonforkkkk-fork"> |
|
304 | 304 | <span>Summary</span> |
|
305 | 305 | </a> |
|
306 | 306 | </li> |
|
307 | 307 | <li> |
|
308 | 308 | <a title="Changelog" href="/anothercpythonforkkkk-fork/changelog"> |
|
309 | 309 | <span>Changelog</span> |
|
310 | 310 | </a> |
|
311 | 311 | </li> |
|
312 | 312 | <li> |
|
313 | 313 | <a title="Files" href="/anothercpythonforkkkk-fork/files/tip/"> |
|
314 | 314 | <span>Files</span> |
|
315 | 315 | </a> |
|
316 | 316 | </li> |
|
317 | 317 | <li> |
|
318 | 318 | <a title="Fork" href="/anothercpythonforkkkk-fork/fork"> |
|
319 | 319 | <span>Fork</span> |
|
320 | 320 | </a> |
|
321 | 321 | </li> |
|
322 | 322 | </ul> |
|
323 | 323 | </div> |
|
324 | 324 | </td> |
|
325 | 325 | <td></td> |
|
326 | 326 | </tr> |
|
327 | 327 | </tbody> |
|
328 | 328 | </table> |
|
329 | 329 | <script>quick_repo_menu();</script> |
|
330 | 330 | <table class="rctable examples"> |
|
331 | 331 | <tbody> |
|
332 | 332 | <tr> |
|
333 | 333 | <td>td-description</td> |
|
334 | 334 | <td>Any description. They may be rather long, and using the expand_commit outlined below is recommended.</td> |
|
335 | 335 | <td class="td-description"> |
|
336 | 336 | Ultrices mattis! Enim pellentesque lacus, sit magna natoque risus turpis ut, auctor ultrices facilisis dapibus odio? Parturient! Porta egestas nascetur, quis, elementum dolor, in magna ac dis sit etiam turpis, scelerisque! Integer tristique aliquam. |
|
337 | 337 | </td> |
|
338 | 338 | </tr> |
|
339 | 339 | </tbody> |
|
340 | 340 | </table> |
|
341 | 341 | <table id="changesets" class="rctable examples end"> |
|
342 | 342 | <tbody> |
|
343 | 343 | <tr> |
|
344 | 344 | <td>expand_commit</td> |
|
345 | 345 | <td>Expands a long message; see html+js.</td> |
|
346 | 346 | <td class="expand_commit" data-commit-id="2ffc6faabc7a9c790b1b452943a3f0c047b8b436" title="Expand commit message"> |
|
347 | 347 | <div class="show_more_col"> |
|
348 | 348 | <i class="show_more"></i> |
|
349 | 349 | </div> |
|
350 | 350 | </td> |
|
351 | 351 | <td class="mid td-description"> |
|
352 | 352 | <div class="log-container truncate-wrap"> |
|
353 | 353 | <div id="c-2ffc6faabc7a9c790b1b452943a3f0c047b8b436" class="message truncate" data-message-raw="tests: Test echo method on the server object |
|
354 | 354 | |
|
355 | 355 | This only works for Pyro4 so far, have to extend it still for HTTP to work.">tests: Test echo method on the server object |
|
356 | 356 | |
|
357 | 357 | This only works for Pyro4 so far, have to extend it still for HTTP to work.</div> |
|
358 | 358 | </div> |
|
359 | 359 | </td> |
|
360 | 360 | </tr> |
|
361 | 361 | </tbody> |
|
362 | 362 | </table> |
|
363 | 363 | <script type="text/javascript"> |
|
364 | 364 | var cache = {} |
|
365 | 365 | $('.expand_commit').on('click',function(e){ |
|
366 | 366 | var target_expand = $(this); |
|
367 | 367 | var cid = target_expand.data('commitId'); |
|
368 | 368 | |
|
369 | 369 | if (target_expand.hasClass('open')){ |
|
370 | 370 | $('#c-'+cid).css({'height': '1.5em', 'white-space': 'nowrap', 'text-overflow': 'ellipsis', 'overflow':'hidden'}); |
|
371 | 371 | $('#t-'+cid).css({'height': '1.5em', 'max-height': '1.5em', 'text-overflow': 'ellipsis', 'overflow':'hidden', 'white-space':'nowrap'}); |
|
372 | 372 | target_expand.removeClass('open'); |
|
373 | 373 | } |
|
374 | 374 | else { |
|
375 | 375 | $('#c-'+cid).css({'height': 'auto', 'white-space': 'pre-line', 'text-overflow': 'initial', 'overflow':'visible'}); |
|
376 | 376 | $('#t-'+cid).css({'height': 'auto', 'max-height': 'none', 'text-overflow': 'initial', 'overflow':'visible', 'white-space':'normal'}); |
|
377 | 377 | target_expand.addClass('open'); |
|
378 | 378 | } |
|
379 | 379 | }); |
|
380 | 380 | |
|
381 | 381 | </script> |
|
382 | 382 | <p>The following classes currently do not have unique styles applied.</p> |
|
383 | 383 | <table class="rctable examples end"> |
|
384 | 384 | <tbody> |
|
385 | 385 | <tr> |
|
386 | 386 | <td>td-regex</td> |
|
387 | 387 | <td>Regex patterns</td> |
|
388 | 388 | <td class="td-regex">(?:#)(?P<issue_id>\d+)</td> |
|
389 | 389 | </tr> |
|
390 | 390 | <tr> |
|
391 | 391 | <td>td-url</td> |
|
392 | 392 | <td>Any URL.</td> |
|
393 | 393 | <td class="td-url">https://rhodecode.com</td> |
|
394 | 394 | </tr> |
|
395 | 395 | <tr> |
|
396 | 396 | <td>td-journalaction</td> |
|
397 | 397 | <td>Action listed in a journal</td> |
|
398 | 398 | <td class="td-journalaction">started following repository supervisor-fork-4</td> |
|
399 | 399 | </tr> |
|
400 | 400 | <tr> |
|
401 | 401 | <td>td-iprange</td> |
|
402 | 402 | <td>Any ip address.</td> |
|
403 | 403 | <td class="td-ip">127.0.0.1-127.0.0.10</td> |
|
404 | 404 | </tr> |
|
405 | 405 | <tr> |
|
406 | 406 | <td>td-exp</td> |
|
407 | 407 | <td>Expiration time.</td> |
|
408 | 408 | <td class="td-exp">never</td> |
|
409 | 409 | </tr> |
|
410 | 410 | <tr> |
|
411 | 411 | <td>td-prefix</td> |
|
412 | 412 | <td>Prefixes outlined in settings.</td> |
|
413 | 413 | <td class="td-prefix">ubuntu-92539</td> |
|
414 | 414 | </tr> |
|
415 | 415 | <tr> |
|
416 | 416 | <td>td-cachekey</td> |
|
417 | 417 | <td>Cache key value.</td> |
|
418 | 418 | <td class="td-cachekey">ubuntu-92539supervisor</td> |
|
419 | 419 | </tr> |
|
420 | 420 | <tr> |
|
421 | 421 | <td>td-email</td> |
|
422 | 422 | <td>Any email address.</td> |
|
423 | 423 | <td class="td-email">example@rhodecode.com</td> |
|
424 | 424 | </tr> |
|
425 | 425 | <tr> |
|
426 | 426 | <td>td-active</td> |
|
427 | 427 | <td>Shows active state with icon-true/icon-false.</td> |
|
428 | 428 | <td class="td-active"><i class="icon-false"></i></td> |
|
429 | 429 | </tr> |
|
430 | 430 | <tr> |
|
431 | 431 | <td>td-size</td> |
|
432 | 432 | <td>File, repo, or directory size.</td> |
|
433 | 433 | <td class="td-size">89 MB</td> |
|
434 | 434 | </tr> |
|
435 | 435 | <tr> |
|
436 | 436 | <td>td-number</td> |
|
437 | 437 | <td>Any numerical data.</td> |
|
438 | 438 | <td class="td-number">42</td> |
|
439 | 439 | </tr> |
|
440 | 440 | <tr> |
|
441 | 441 | <td>td-message</td> |
|
442 | 442 | <td>Any commit message. Often treated with the truncate class used for descriptions as well.</td> |
|
443 | 443 | <td class="td-message">Updated the files</td> |
|
444 | 444 | </tr> |
|
445 | 445 | </tbody> |
|
446 | 446 | </table> |
|
447 | 447 | |
|
448 | 448 | |
|
449 | 449 | <h2>Permissions table</h2> |
|
450 | 450 | |
|
451 | 451 | <p> |
|
452 | 452 | This is a special-case table; it has |
|
453 | 453 | <code>table class="rctable permissions"</code> |
|
454 | 454 | where "rctable" applies the rhodecode styling as above, and |
|
455 | 455 | "permissions" adds an extra layer of customization specific to |
|
456 | 456 | permissions tables. Other special-case tables may exist or be |
|
457 | 457 | created if necessary. |
|
458 | 458 | </p> |
|
459 | 459 | |
|
460 | 460 | <table class="rctable permissions"> |
|
461 | 461 | <tr> |
|
462 | 462 | <th class="td-radio">none</th> |
|
463 | 463 | <th class="td-radio">read</th> |
|
464 | 464 | <th class="td-radio">write</th> |
|
465 | 465 | <th class="td-radio">admin</th> |
|
466 | 466 | <th>user/user group</th> |
|
467 | 467 | <th></th> |
|
468 | 468 | </tr> |
|
469 | 469 | <tr class="perm_admin_row"> |
|
470 | 470 | <td class="td-radio"><input type="radio" value="repository.none" |
|
471 | 471 | name="admin_perm_2" id="admin_perm_2_repositorynone" |
|
472 | 472 | disabled="disabled"></td> |
|
473 | 473 | <td class="td-radio"><input type="radio" value="repository.read" |
|
474 | 474 | name="admin_perm_2" id="admin_perm_2_repositoryread" |
|
475 | 475 | disabled="disabled"></td> |
|
476 | 476 | <td class="td-radio"><input type="radio" value="repository.write" |
|
477 | 477 | name="admin_perm_2" id="admin_perm_2_repositorywrite" |
|
478 | 478 | disabled="disabled"></td> |
|
479 | 479 | <td class="td-radio"><input type="radio" value="repository.admin" |
|
480 | 480 | name="admin_perm_2" id="admin_perm_2_repositoryadmin" |
|
481 | 481 | disabled="disabled" checked="checked"></td> |
|
482 | 482 | <td> |
|
483 | 483 | <img class="gravatar" src="https://secure.gravatar.com/avatar/be9d18f611892a738e54f2a3a171e2f9?d=identicon&s=32" height="16" width="16"> |
|
484 | 484 | <span class="user">dev (super admin) (owner)</span> |
|
485 | 485 | </td> |
|
486 | 486 | <td></td> |
|
487 | 487 | </tr> |
|
488 | 488 | <tr> |
|
489 | 489 | <td colspan="4"> |
|
490 | 490 | <span class="private_repo_msg"> |
|
491 | 491 | private repository |
|
492 | 492 | </span> |
|
493 | 493 | </td> |
|
494 | 494 | <td class="private_repo_msg"> |
|
495 | 495 | <i class="icon-user"></i> |
|
496 | 496 | default - only people explicitly added here will have access</td> |
|
497 | 497 | <td></td> |
|
498 | 498 | </tr> |
|
499 | 499 | <tr> |
|
500 | 500 | <td class="td-radio"><input type="radio" value="repository.none" |
|
501 | 501 | name="u_perm_1" id="u_perm_1_repositorynone"></td> |
|
502 | 502 | <td class="td-radio"><input type="radio" checked="checked" |
|
503 | 503 | value="repository.read" name="u_perm_1" |
|
504 | 504 | id="u_perm_1_repositoryread"></td> |
|
505 | 505 | <td class="td-radio"><input type="radio" value="repository.write" |
|
506 | 506 | name="u_perm_1" id="u_perm_1_repositorywrite"></td> |
|
507 | 507 | <td class="td-radio"><input type="radio" value="repository.admin" |
|
508 | 508 | name="u_perm_1" id="u_perm_1_repositoryadmin"></td> |
|
509 | 509 | <td> |
|
510 | 510 | <img class="gravatar" src="/_static/rhodecode/images/user30.png" height="16" width="16"> |
|
511 | 511 | <span class="user">default</span> |
|
512 | 512 | </td> |
|
513 | 513 | <td></td> |
|
514 | 514 | </tr> |
|
515 | 515 | <tr> |
|
516 | 516 | <td class="td-radio"><input type="radio" value="repository.none" |
|
517 | 517 | name="u_perm_2" id="u_perm_2_repositorynone"></td> |
|
518 | 518 | <td class="td-radio"><input type="radio" checked="checked" |
|
519 | 519 | value="repository.read" name="u_perm_2" |
|
520 | 520 | id="u_perm_2_repositoryread"></td> |
|
521 | 521 | <td class="td-radio"><input type="radio" value="repository.write" |
|
522 | 522 | name="u_perm_2" id="u_perm_2_repositorywrite"></td> |
|
523 | 523 | <td class="td-radio"><input type="radio" value="repository.admin" |
|
524 | 524 | name="u_perm_2" id="u_perm_2_repositoryadmin"></td> |
|
525 | 525 | <td> |
|
526 | 526 | <img class="gravatar" src="https://secure.gravatar.com/avatar/be9d18f611892a738e54f2a3a171e2f9?d=identicon&s=32" height="16" width="16"> |
|
527 | 527 | <a class="user" href="/_admin/users/2/edit">dev</a> |
|
528 | 528 | </td> |
|
529 | 529 | <td> |
|
530 | 530 | <span member_type="user" member="2" |
|
531 | 531 | class="btn action_button btn-link btn-danger">revoke</span> |
|
532 | 532 | </td> |
|
533 | 533 | </tr> |
|
534 | 534 | </tbody> |
|
535 | 535 | </table> |
|
536 | 536 | <div class="link" id="add_perm"> |
|
537 | 537 | Add new |
|
538 | 538 | </div> |
|
539 | 539 | |
|
540 | 540 | |
|
541 | 541 | |
|
542 | 542 | </div> |
|
543 | 543 | </div> |
|
544 | 544 | </div> |
|
545 | 545 | </%def> |
@@ -1,605 +1,637 b'' | |||
|
1 | 1 | <%inherit file="/base/base.html"/> |
|
2 | 2 | |
|
3 | 3 | <%def name="title()"> |
|
4 | 4 | ${_('%s Pull Request #%s') % (c.repo_name, c.pull_request.pull_request_id)} |
|
5 | 5 | %if c.rhodecode_name: |
|
6 | 6 | · ${h.branding(c.rhodecode_name)} |
|
7 | 7 | %endif |
|
8 | 8 | </%def> |
|
9 | 9 | |
|
10 | 10 | <%def name="breadcrumbs_links()"> |
|
11 | 11 | <span id="pr-title"> |
|
12 | 12 | ${c.pull_request.title} |
|
13 | 13 | %if c.pull_request.is_closed(): |
|
14 | 14 | (${_('Closed')}) |
|
15 | 15 | %endif |
|
16 | 16 | </span> |
|
17 | 17 | <div id="pr-title-edit" class="input" style="display: none;"> |
|
18 | 18 | ${h.text('pullrequest_title', id_="pr-title-input", class_="large", value=c.pull_request.title)} |
|
19 | 19 | </div> |
|
20 | 20 | </%def> |
|
21 | 21 | |
|
22 | 22 | <%def name="menu_bar_nav()"> |
|
23 | 23 | ${self.menu_items(active='repositories')} |
|
24 | 24 | </%def> |
|
25 | 25 | |
|
26 | 26 | <%def name="menu_bar_subnav()"> |
|
27 | 27 | ${self.repo_menu(active='showpullrequest')} |
|
28 | 28 | </%def> |
|
29 | 29 | |
|
30 | 30 | <%def name="main()"> |
|
31 | 31 | <script type="text/javascript"> |
|
32 | 32 | // TODO: marcink switch this to pyroutes |
|
33 | 33 | AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}"; |
|
34 | 34 | templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id}; |
|
35 | 35 | </script> |
|
36 | 36 | <div class="box"> |
|
37 | 37 | <div class="title"> |
|
38 | 38 | ${self.repo_page_title(c.rhodecode_db_repo)} |
|
39 | 39 | </div> |
|
40 | 40 | |
|
41 | 41 | ${self.breadcrumbs()} |
|
42 | 42 | |
|
43 | 43 | |
|
44 | 44 | <div class="box pr-summary"> |
|
45 | 45 | <div class="summary-details block-left"> |
|
46 | 46 | <%summary = lambda n:{False:'summary-short'}.get(n)%> |
|
47 | 47 | <div class="pr-details-title"> |
|
48 | 48 | <a href="${h.url('pull_requests_global', pull_request_id=c.pull_request.pull_request_id)}">${_('Pull request #%s') % c.pull_request.pull_request_id}</a> ${_('From')} ${h.format_date(c.pull_request.created_on)} |
|
49 | 49 | %if c.allowed_to_update: |
|
50 | 50 | <div id="delete_pullrequest" class="pull-right action_button ${'' if c.allowed_to_delete else 'disabled' }" style="clear:inherit;padding: 0"> |
|
51 | 51 | % if c.allowed_to_delete: |
|
52 | 52 | ${h.secure_form(url('pullrequest_delete', repo_name=c.pull_request.target_repo.repo_name, pull_request_id=c.pull_request.pull_request_id),method='delete')} |
|
53 | 53 | ${h.submit('remove_%s' % c.pull_request.pull_request_id, _('Delete'), |
|
54 | 54 | class_="btn btn-link btn-danger",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")} |
|
55 | 55 | ${h.end_form()} |
|
56 | 56 | % else: |
|
57 | 57 | ${_('Delete')} |
|
58 | 58 | % endif |
|
59 | 59 | </div> |
|
60 | 60 | <div id="open_edit_pullrequest" class="pull-right action_button">${_('Edit')}</div> |
|
61 |
<div id="close_edit_pullrequest" class="pull-right action_button" style="display: none;padding: 0">${_('Cancel |
|
|
61 | <div id="close_edit_pullrequest" class="pull-right action_button" style="display: none;padding: 0">${_('Cancel')}</div> | |
|
62 | 62 | %endif |
|
63 | 63 | </div> |
|
64 | 64 | |
|
65 | 65 | <div id="summary" class="fields pr-details-content"> |
|
66 | 66 | <div class="field"> |
|
67 | 67 | <div class="label-summary"> |
|
68 | 68 | <label>${_('Origin')}:</label> |
|
69 | 69 | </div> |
|
70 | 70 | <div class="input"> |
|
71 | 71 | <div class="pr-origininfo"> |
|
72 | 72 | ## branch link is only valid if it is a branch |
|
73 | 73 | <span class="tag"> |
|
74 | 74 | %if c.pull_request.source_ref_parts.type == 'branch': |
|
75 | 75 | <a href="${h.url('changelog_home', repo_name=c.pull_request.source_repo.repo_name, branch=c.pull_request.source_ref_parts.name)}">${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}</a> |
|
76 | 76 | %else: |
|
77 | 77 | ${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name} |
|
78 | 78 | %endif |
|
79 | 79 | </span> |
|
80 | 80 | <span class="clone-url"> |
|
81 | 81 | <a href="${h.url('summary_home', repo_name=c.pull_request.source_repo.repo_name)}">${c.pull_request.source_repo.clone_url()}</a> |
|
82 | 82 | </span> |
|
83 | 83 | </div> |
|
84 | 84 | <div class="pr-pullinfo"> |
|
85 | 85 | %if h.is_hg(c.pull_request.source_repo): |
|
86 | 86 | <input type="text" value="hg pull -r ${h.short_id(c.source_ref)} ${c.pull_request.source_repo.clone_url()}" readonly="readonly"> |
|
87 | 87 | %elif h.is_git(c.pull_request.source_repo): |
|
88 | 88 | <input type="text" value="git pull ${c.pull_request.source_repo.clone_url()} ${c.pull_request.source_ref_parts.name}" readonly="readonly"> |
|
89 | 89 | %endif |
|
90 | 90 | </div> |
|
91 | 91 | </div> |
|
92 | 92 | </div> |
|
93 | 93 | <div class="field"> |
|
94 | 94 | <div class="label-summary"> |
|
95 | 95 | <label>${_('Target')}:</label> |
|
96 | 96 | </div> |
|
97 | 97 | <div class="input"> |
|
98 | 98 | <div class="pr-targetinfo"> |
|
99 | 99 | ## branch link is only valid if it is a branch |
|
100 | 100 | <span class="tag"> |
|
101 | 101 | %if c.pull_request.target_ref_parts.type == 'branch': |
|
102 | 102 | <a href="${h.url('changelog_home', repo_name=c.pull_request.target_repo.repo_name, branch=c.pull_request.target_ref_parts.name)}">${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}</a> |
|
103 | 103 | %else: |
|
104 | 104 | ${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name} |
|
105 | 105 | %endif |
|
106 | 106 | </span> |
|
107 | 107 | <span class="clone-url"> |
|
108 | 108 | <a href="${h.url('summary_home', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.clone_url()}</a> |
|
109 | 109 | </span> |
|
110 | 110 | </div> |
|
111 | 111 | </div> |
|
112 | 112 | </div> |
|
113 | 113 | |
|
114 | 114 | ## Link to the shadow repository. |
|
115 | 115 | <div class="field"> |
|
116 | 116 | <div class="label-summary"> |
|
117 | 117 | <label>${_('Merge')}:</label> |
|
118 | 118 | </div> |
|
119 | 119 | <div class="input"> |
|
120 | 120 | % if not c.pull_request.is_closed() and c.pull_request.shadow_merge_ref: |
|
121 | 121 | <div class="pr-mergeinfo"> |
|
122 | 122 | %if h.is_hg(c.pull_request.target_repo): |
|
123 | 123 | <input type="text" value="hg clone -u ${c.pull_request.shadow_merge_ref.name} ${c.shadow_clone_url} pull-request-${c.pull_request.pull_request_id}" readonly="readonly"> |
|
124 | 124 | %elif h.is_git(c.pull_request.target_repo): |
|
125 | 125 | <input type="text" value="git clone --branch ${c.pull_request.shadow_merge_ref.name} ${c.shadow_clone_url} pull-request-${c.pull_request.pull_request_id}" readonly="readonly"> |
|
126 | 126 | %endif |
|
127 | 127 | </div> |
|
128 | 128 | % else: |
|
129 | 129 | <div class=""> |
|
130 | 130 | ${_('Shadow repository data not available')}. |
|
131 | 131 | </div> |
|
132 | 132 | % endif |
|
133 | 133 | </div> |
|
134 | 134 | </div> |
|
135 | 135 | |
|
136 | 136 | <div class="field"> |
|
137 | 137 | <div class="label-summary"> |
|
138 | 138 | <label>${_('Review')}:</label> |
|
139 | 139 | </div> |
|
140 | 140 | <div class="input"> |
|
141 | 141 | %if c.pull_request_review_status: |
|
142 | 142 | <div class="${'flag_status %s' % c.pull_request_review_status} tooltip pull-left"></div> |
|
143 | 143 | <span class="changeset-status-lbl tooltip"> |
|
144 | 144 | %if c.pull_request.is_closed(): |
|
145 | 145 | ${_('Closed')}, |
|
146 | 146 | %endif |
|
147 | 147 | ${h.commit_status_lbl(c.pull_request_review_status)} |
|
148 | 148 | </span> |
|
149 | 149 | - ${ungettext('calculated based on %s reviewer vote', 'calculated based on %s reviewers votes', len(c.pull_request_reviewers)) % len(c.pull_request_reviewers)} |
|
150 | 150 | %endif |
|
151 | 151 | </div> |
|
152 | 152 | </div> |
|
153 | 153 | <div class="field"> |
|
154 | 154 | <div class="pr-description-label label-summary"> |
|
155 | 155 | <label>${_('Description')}:</label> |
|
156 | 156 | </div> |
|
157 | 157 | <div id="pr-desc" class="input"> |
|
158 | 158 | <div class="pr-description">${h.urlify_commit_message(c.pull_request.description, c.repo_name)}</div> |
|
159 | 159 | </div> |
|
160 | 160 | <div id="pr-desc-edit" class="input textarea editor" style="display: none;"> |
|
161 | 161 | <textarea id="pr-description-input" size="30">${c.pull_request.description}</textarea> |
|
162 | 162 | </div> |
|
163 | 163 | </div> |
|
164 | <div class="field"> | |
|
165 | <div class="label-summary"> | |
|
166 | <label>${_('Comments')}:</label> | |
|
167 | </div> | |
|
168 | <div class="input"> | |
|
169 | <div> | |
|
170 | <div class="comments-number"> | |
|
171 | %if c.comments: | |
|
172 | <a href="#comments">${ungettext("%d General Comment", "%d General Comments", len(c.comments)) % len(c.comments)}</a>, | |
|
173 | %else: | |
|
174 | ${ungettext("%d General Comment", "%d General Comments", len(c.comments)) % len(c.comments)} | |
|
175 | %endif | |
|
176 | ||
|
177 | %if c.inline_cnt: | |
|
178 | <a href="#" onclick="return Rhodecode.comments.nextComment();" id="inline-comments-counter">${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}</a> | |
|
179 | %else: | |
|
180 | ${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt} | |
|
181 | %endif | |
|
182 | ||
|
183 | %if c.outdated_cnt: | |
|
184 | , ${ungettext("%d Outdated Comment", "%d Outdated Comments", c.outdated_cnt) % c.outdated_cnt} <span id="show-outdated-comments" class="btn btn-link">${_('(Show)')}</span> | |
|
185 | %endif | |
|
186 | </div> | |
|
187 | </div> | |
|
188 | </div> | |
|
189 | ||
|
190 | </div> | |
|
191 | ||
|
192 | <style> | |
|
193 | .pr-versions { | |
|
194 | position: relative; | |
|
195 | top: 6px; | |
|
196 | } | |
|
197 | </style> | |
|
198 | 164 | |
|
199 | 165 | <div class="field"> |
|
200 | 166 | <div class="label-summary"> |
|
201 | 167 | <label>${_('Versions')} (${len(c.versions)+1}):</label> |
|
202 | 168 | </div> |
|
203 | 169 | |
|
204 | 170 | <div class="pr-versions"> |
|
205 | 171 | % if c.show_version_changes: |
|
206 | 172 | <table> |
|
207 | ## current visible version | |
|
208 |
<tr class="version-pr" style="display: ${'' if c.at_version |
|
|
173 | ## CURRENTLY SELECT PR VERSION | |
|
174 | <tr class="version-pr" style="display: ${'' if c.at_version_num is None else 'none'}"> | |
|
209 | 175 | <td> |
|
210 | 176 | % if c.at_version in [None, 'latest']: |
|
211 | 177 | <i class="icon-ok link"></i> |
|
178 | % else: | |
|
179 | <i class="icon-comment"></i> <code>${len(c.inline_versions[None])}</code> | |
|
212 | 180 | % endif |
|
213 | 181 | </td> |
|
214 | 182 | <td> |
|
215 | 183 | <code> |
|
216 | 184 | % if c.versions: |
|
217 | 185 | <a href="${h.url.current(version='latest')}">${_('latest')}</a> |
|
218 | 186 | % else: |
|
219 | 187 | ${_('initial')} |
|
220 | 188 | % endif |
|
221 | 189 | </code> |
|
222 | 190 | </td> |
|
223 | 191 | <td> |
|
224 | 192 | <code>${c.pull_request_latest.source_ref_parts.commit_id[:6]}</code> |
|
225 | 193 | </td> |
|
226 | <td>${_('created')} ${h.age_component(c.pull_request_latest.updated_on)}</td> | |
|
227 | 194 | <td> |
|
228 | % if c.versions and c.at_version in [None, 'latest']: | |
|
229 | <span id="show-pr-versions" class="btn btn-link" onclick="$('.version-pr').show(); $(this).hide(); return false">${_('(Show all)')}</span> | |
|
195 | ${_('created')} ${h.age_component(c.pull_request_latest.updated_on)} | |
|
196 | </td> | |
|
197 | <td align="right"> | |
|
198 | % if c.versions and c.at_version_num in [None, 'latest']: | |
|
199 | <span id="show-pr-versions" class="btn btn-link" onclick="$('.version-pr').show(); $(this).hide(); return false">${_('Show all versions')}</span> | |
|
230 | 200 | % endif |
|
231 | 201 | </td> |
|
232 | 202 | </tr> |
|
233 | 203 | |
|
204 | ## SHOW ALL VERSIONS OF PR | |
|
205 | <% ver_pr = None %> | |
|
234 | 206 | % for ver in reversed(c.pull_request.versions()): |
|
235 |
< |
|
|
207 | <% ver_pr = ver.pull_request_version_id %> | |
|
208 | <tr class="version-pr" style="display: ${'' if c.at_version == ver_pr else 'none'}"> | |
|
236 | 209 | <td> |
|
237 |
% if c.at_version == ver |
|
|
210 | % if c.at_version == ver_pr: | |
|
238 | 211 | <i class="icon-ok link"></i> |
|
212 | % else: | |
|
213 | <i class="icon-comment"></i> <code>${len(c.inline_versions[ver_pr])}</code> | |
|
239 | 214 | % endif |
|
240 | 215 | </td> |
|
241 | <td><code><a href="${h.url.current(version=ver.pull_request_version_id)}">version ${ver.pull_request_version_id}</a></code></td> | |
|
216 | <td> | |
|
217 | <code><a href="${h.url.current(version=ver_pr)}">version ${ver_pr}</a></code> | |
|
218 | </td> | |
|
242 | 219 | <td> |
|
243 | 220 | <code>${ver.source_ref_parts.commit_id[:6]}</code> |
|
244 | 221 | </td> |
|
245 | <td>${_('created')} ${h.age_component(ver.updated_on)}</td> | |
|
246 | 222 | <td> |
|
247 | % if c.at_version == ver.pull_request_version_id: | |
|
248 | <span id="show-pr-versions" class="btn btn-link" onclick="$('.version-pr').show(); $(this).hide(); return false">${_('(Show all)')}</span> | |
|
223 | ${_('created')} ${h.age_component(ver.updated_on)} | |
|
224 | </td> | |
|
225 | <td align="right"> | |
|
226 | % if c.at_version == ver_pr: | |
|
227 | <span id="show-pr-versions" class="btn btn-link" onclick="$('.version-pr').show(); $(this).hide(); return false">${_('Show all versions')}</span> | |
|
249 | 228 | % endif |
|
250 | 229 | </td> |
|
251 | 230 | </tr> |
|
252 | 231 | % endfor |
|
253 | </table> | |
|
232 | ||
|
233 | ## show comment/inline comments summary | |
|
234 | <tr> | |
|
235 | <td> | |
|
236 | </td> | |
|
254 | 237 | |
|
255 | % if c.at_version: | |
|
256 | <pre> | |
|
257 | Changed commits: | |
|
258 | * added: ${len(c.changes.added)} | |
|
259 | * removed: ${len(c.changes.removed)} | |
|
238 | <% inline_comm_count_ver = len(c.inline_versions[ver_pr])%> | |
|
239 | <td colspan="4" style="border-top: 1px dashed #dbd9da"> | |
|
240 | ${_('Comments for this version')}: | |
|
241 | %if c.comments: | |
|
242 | <a href="#comments">${_("%d General ") % len(c.comments)}</a> | |
|
243 | %else: | |
|
244 | ${_("%d General ") % len(c.comments)} | |
|
245 | %endif | |
|
246 | ||
|
247 | <% inline_comm_count_ver = len(c.inline_versions[c.at_version_num])%> | |
|
248 | %if inline_comm_count_ver: | |
|
249 | , <a href="#" onclick="return Rhodecode.comments.nextComment();" id="inline-comments-counter">${_("%d Inline") % inline_comm_count_ver}</a> | |
|
250 | %else: | |
|
251 | , ${_("%d Inline") % inline_comm_count_ver} | |
|
252 | %endif | |
|
260 | 253 | |
|
261 | % if not (c.file_changes.added+c.file_changes.modified+c.file_changes.removed): | |
|
262 | No file changes found | |
|
263 | % else: | |
|
264 | Changed files: | |
|
265 | %for file_name in c.file_changes.added: | |
|
266 | * A <a href="#${'a_' + h.FID('', file_name)}">${file_name}</a> | |
|
267 | %endfor | |
|
268 | %for file_name in c.file_changes.modified: | |
|
269 | * M <a href="#${'a_' + h.FID('', file_name)}">${file_name}</a> | |
|
270 | %endfor | |
|
271 | %for file_name in c.file_changes.removed: | |
|
272 | * R ${file_name} | |
|
273 | %endfor | |
|
274 | % endif | |
|
254 | %if c.outdated_cnt: | |
|
255 | , <a href="#" onclick="showOutdated(); Rhodecode.comments.nextOutdatedComment(); return false;">${_("%d Outdated") % c.outdated_cnt}</a> | |
|
256 | <a href="#" class="showOutdatedComments" onclick="showOutdated(this); return false;"> | ${_('show outdated comments')}</a> | |
|
257 | <a href="#" class="hideOutdatedComments" style="display: none" onclick="hideOutdated(this); return false;"> | ${_('hide outdated comments')}</a> | |
|
258 | %else: | |
|
259 | , ${_("%d Outdated") % c.outdated_cnt} | |
|
260 | %endif | |
|
261 | </td> | |
|
262 | </tr> | |
|
263 | ||
|
264 | <tr> | |
|
265 | <td></td> | |
|
266 | <td colspan="4"> | |
|
267 | % if c.at_version: | |
|
268 | <pre> | |
|
269 | Changed commits: | |
|
270 | * added: ${len(c.changes.added)} | |
|
271 | * removed: ${len(c.changes.removed)} | |
|
272 | ||
|
273 | % if not (c.file_changes.added+c.file_changes.modified+c.file_changes.removed): | |
|
274 | No file changes found | |
|
275 | % else: | |
|
276 | Changed files: | |
|
277 | %for file_name in c.file_changes.added: | |
|
278 | * A <a href="#${'a_' + h.FID('', file_name)}">${file_name}</a> | |
|
279 | %endfor | |
|
280 | %for file_name in c.file_changes.modified: | |
|
281 | * M <a href="#${'a_' + h.FID('', file_name)}">${file_name}</a> | |
|
282 | %endfor | |
|
283 | %for file_name in c.file_changes.removed: | |
|
284 | * R ${file_name} | |
|
285 | %endfor | |
|
286 | % endif | |
|
275 | 287 | </pre> |
|
276 | % endif | |
|
288 | % endif | |
|
289 | </td> | |
|
290 | </tr> | |
|
291 | </table> | |
|
277 | 292 | % else: |
|
278 | 293 | ${_('Pull request versions not available')}. |
|
279 | 294 | % endif |
|
280 | 295 | </div> |
|
281 | 296 | </div> |
|
282 | 297 | |
|
283 | 298 | <div id="pr-save" class="field" style="display: none;"> |
|
284 | 299 | <div class="label-summary"></div> |
|
285 | 300 | <div class="input"> |
|
286 | 301 | <span id="edit_pull_request" class="btn btn-small">${_('Save Changes')}</span> |
|
287 | 302 | </div> |
|
288 | 303 | </div> |
|
289 | 304 | </div> |
|
290 | 305 | </div> |
|
291 | 306 | <div> |
|
292 | 307 | ## AUTHOR |
|
293 | 308 | <div class="reviewers-title block-right"> |
|
294 | 309 | <div class="pr-details-title"> |
|
295 | 310 | ${_('Author')} |
|
296 | 311 | </div> |
|
297 | 312 | </div> |
|
298 | 313 | <div class="block-right pr-details-content reviewers"> |
|
299 | 314 | <ul class="group_members"> |
|
300 | 315 | <li> |
|
301 | 316 | ${self.gravatar_with_user(c.pull_request.author.email, 16)} |
|
302 | 317 | </li> |
|
303 | 318 | </ul> |
|
304 | 319 | </div> |
|
305 | 320 | ## REVIEWERS |
|
306 | 321 | <div class="reviewers-title block-right"> |
|
307 | 322 | <div class="pr-details-title"> |
|
308 | 323 | ${_('Pull request reviewers')} |
|
309 | 324 | %if c.allowed_to_update: |
|
310 | 325 | <span id="open_edit_reviewers" class="block-right action_button">${_('Edit')}</span> |
|
311 | 326 | <span id="close_edit_reviewers" class="block-right action_button" style="display: none;">${_('Close')}</span> |
|
312 | 327 | %endif |
|
313 | 328 | </div> |
|
314 | 329 | </div> |
|
315 | 330 | <div id="reviewers" class="block-right pr-details-content reviewers"> |
|
316 | 331 | ## members goes here ! |
|
317 | 332 | <input type="hidden" name="__start__" value="review_members:sequence"> |
|
318 | 333 | <ul id="review_members" class="group_members"> |
|
319 | 334 | %for member,reasons,status in c.pull_request_reviewers: |
|
320 | 335 | <li id="reviewer_${member.user_id}"> |
|
321 | 336 | <div class="reviewers_member"> |
|
322 | 337 | <div class="reviewer_status tooltip" title="${h.tooltip(h.commit_status_lbl(status[0][1].status if status else 'not_reviewed'))}"> |
|
323 | 338 | <div class="${'flag_status %s' % (status[0][1].status if status else 'not_reviewed')} pull-left reviewer_member_status"></div> |
|
324 | 339 | </div> |
|
325 | 340 | <div id="reviewer_${member.user_id}_name" class="reviewer_name"> |
|
326 | 341 | ${self.gravatar_with_user(member.email, 16)} |
|
327 | 342 | </div> |
|
328 | 343 | <input type="hidden" name="__start__" value="reviewer:mapping"> |
|
329 | 344 | <input type="hidden" name="__start__" value="reasons:sequence"> |
|
330 | 345 | %for reason in reasons: |
|
331 | 346 | <div class="reviewer_reason">- ${reason}</div> |
|
332 | 347 | <input type="hidden" name="reason" value="${reason}"> |
|
333 | 348 | |
|
334 | 349 | %endfor |
|
335 | 350 | <input type="hidden" name="__end__" value="reasons:sequence"> |
|
336 | 351 | <input id="reviewer_${member.user_id}_input" type="hidden" value="${member.user_id}" name="user_id" /> |
|
337 | 352 | <input type="hidden" name="__end__" value="reviewer:mapping"> |
|
338 | 353 | %if c.allowed_to_update: |
|
339 | 354 | <div class="reviewer_member_remove action_button" onclick="removeReviewMember(${member.user_id}, true)" style="visibility: hidden;"> |
|
340 | 355 | <i class="icon-remove-sign" ></i> |
|
341 | 356 | </div> |
|
342 | 357 | %endif |
|
343 | 358 | </div> |
|
344 | 359 | </li> |
|
345 | 360 | %endfor |
|
346 | 361 | </ul> |
|
347 | 362 | <input type="hidden" name="__end__" value="review_members:sequence"> |
|
348 | 363 | %if not c.pull_request.is_closed(): |
|
349 | 364 | <div id="add_reviewer_input" class='ac' style="display: none;"> |
|
350 | 365 | %if c.allowed_to_update: |
|
351 | 366 | <div class="reviewer_ac"> |
|
352 | 367 | ${h.text('user', class_='ac-input', placeholder=_('Add reviewer'))} |
|
353 | 368 | <div id="reviewers_container"></div> |
|
354 | 369 | </div> |
|
355 | 370 | <div> |
|
356 | 371 | <span id="update_pull_request" class="btn btn-small">${_('Save Changes')}</span> |
|
357 | 372 | </div> |
|
358 | 373 | %endif |
|
359 | 374 | </div> |
|
360 | 375 | %endif |
|
361 | 376 | </div> |
|
362 | 377 | </div> |
|
363 | 378 | </div> |
|
364 | 379 | <div class="box"> |
|
365 | 380 | ##DIFF |
|
366 | 381 | <div class="table" > |
|
367 | 382 | <div id="changeset_compare_view_content"> |
|
368 | 383 | ##CS |
|
369 | 384 | % if c.missing_requirements: |
|
370 | 385 | <div class="box"> |
|
371 | 386 | <div class="alert alert-warning"> |
|
372 | 387 | <div> |
|
373 | 388 | <strong>${_('Missing requirements:')}</strong> |
|
374 | 389 | ${_('These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.')} |
|
375 | 390 | </div> |
|
376 | 391 | </div> |
|
377 | 392 | </div> |
|
378 | 393 | % elif c.missing_commits: |
|
379 | 394 | <div class="box"> |
|
380 | 395 | <div class="alert alert-warning"> |
|
381 | 396 | <div> |
|
382 | 397 | <strong>${_('Missing commits')}:</strong> |
|
383 | 398 | ${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')} |
|
384 | 399 | ${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')} |
|
385 | 400 | </div> |
|
386 | 401 | </div> |
|
387 | 402 | </div> |
|
388 | 403 | % endif |
|
389 | 404 | <div class="compare_view_commits_title"> |
|
390 | 405 | |
|
391 | 406 | <div class="pull-left"> |
|
392 | 407 | <div class="btn-group"> |
|
393 | 408 | <a |
|
394 | 409 | class="btn" |
|
395 | 410 | href="#" |
|
396 | 411 | onclick="$('.compare_select').show();$('.compare_select_hidden').hide(); return false"> |
|
397 | 412 | ${ungettext('Expand %s commit','Expand %s commits', len(c.commit_ranges)) % len(c.commit_ranges)} |
|
398 | 413 | </a> |
|
399 | 414 | <a |
|
400 | 415 | class="btn" |
|
401 | 416 | href="#" |
|
402 | 417 | onclick="$('.compare_select').hide();$('.compare_select_hidden').show(); return false"> |
|
403 | 418 | ${ungettext('Collapse %s commit','Collapse %s commits', len(c.commit_ranges)) % len(c.commit_ranges)} |
|
404 | 419 | </a> |
|
405 | 420 | </div> |
|
406 | 421 | </div> |
|
407 | 422 | |
|
408 | 423 | <div class="pull-right"> |
|
409 | 424 | % if c.allowed_to_update and not c.pull_request.is_closed(): |
|
410 | 425 | <a id="update_commits" class="btn btn-primary pull-right">${_('Update commits')}</a> |
|
411 | 426 | % else: |
|
412 | 427 | <a class="tooltip btn disabled pull-right" disabled="disabled" title="${_('Update is disabled for current view')}">${_('Update commits')}</a> |
|
413 | 428 | % endif |
|
414 | 429 | |
|
415 | 430 | </div> |
|
416 | 431 | |
|
417 | 432 | </div> |
|
418 | 433 | % if not c.missing_commits: |
|
419 | 434 | <%include file="/compare/compare_commits.html" /> |
|
420 | 435 | <div class="cs_files"> |
|
421 | 436 | <%namespace name="cbdiffs" file="/codeblocks/diffs.html"/> |
|
422 | 437 | ${cbdiffs.render_diffset_menu()} |
|
423 | 438 | ${cbdiffs.render_diffset( |
|
424 | 439 | c.diffset, use_comments=True, |
|
425 | 440 | collapse_when_files_over=30, |
|
426 |
disable_new_comments=not c.allowed_to_comment |
|
|
441 | disable_new_comments=not c.allowed_to_comment, | |
|
442 | deleted_files_comments=c.deleted_files_comments)} | |
|
427 | 443 | |
|
428 | 444 | </div> |
|
429 | 445 | % endif |
|
430 | 446 | </div> |
|
431 | 447 | |
|
432 | 448 | ## template for inline comment form |
|
433 | 449 | <%namespace name="comment" file="/changeset/changeset_file_comment.html"/> |
|
434 | 450 | |
|
435 | 451 | ## render general comments |
|
436 | 452 | ${comment.generate_comments(include_pull_request=True, is_pull_request=True)} |
|
437 | 453 | |
|
438 | 454 | % if not c.pull_request.is_closed(): |
|
439 | 455 | ## main comment form and it status |
|
440 | 456 | ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name, |
|
441 | 457 | pull_request_id=c.pull_request.pull_request_id), |
|
442 | 458 | c.pull_request_review_status, |
|
443 | 459 | is_pull_request=True, change_status=c.allowed_to_change_status)} |
|
444 | 460 | %endif |
|
445 | 461 | |
|
446 | 462 | <script type="text/javascript"> |
|
447 | 463 | if (location.hash) { |
|
448 | 464 | var result = splitDelimitedHash(location.hash); |
|
449 | 465 | var line = $('html').find(result.loc); |
|
450 | 466 | if (line.length > 0){ |
|
451 | 467 | offsetScroll(line, 70); |
|
452 | 468 | } |
|
453 | 469 | } |
|
454 | 470 | $(function(){ |
|
455 | 471 | ReviewerAutoComplete('user'); |
|
456 | 472 | // custom code mirror |
|
457 | 473 | var codeMirrorInstance = initPullRequestsCodeMirror('#pr-description-input'); |
|
458 | 474 | |
|
459 | 475 | var PRDetails = { |
|
460 | 476 | editButton: $('#open_edit_pullrequest'), |
|
461 | 477 | closeButton: $('#close_edit_pullrequest'), |
|
462 | 478 | deleteButton: $('#delete_pullrequest'), |
|
463 | 479 | viewFields: $('#pr-desc, #pr-title'), |
|
464 | 480 | editFields: $('#pr-desc-edit, #pr-title-edit, #pr-save'), |
|
465 | 481 | |
|
466 | 482 | init: function() { |
|
467 | 483 | var that = this; |
|
468 | 484 | this.editButton.on('click', function(e) { that.edit(); }); |
|
469 | 485 | this.closeButton.on('click', function(e) { that.view(); }); |
|
470 | 486 | }, |
|
471 | 487 | |
|
472 | 488 | edit: function(event) { |
|
473 | 489 | this.viewFields.hide(); |
|
474 | 490 | this.editButton.hide(); |
|
475 | 491 | this.deleteButton.hide(); |
|
476 | 492 | this.closeButton.show(); |
|
477 | 493 | this.editFields.show(); |
|
478 | 494 | codeMirrorInstance.refresh(); |
|
479 | 495 | }, |
|
480 | 496 | |
|
481 | 497 | view: function(event) { |
|
482 | 498 | this.editButton.show(); |
|
483 | 499 | this.deleteButton.show(); |
|
484 | 500 | this.editFields.hide(); |
|
485 | 501 | this.closeButton.hide(); |
|
486 | 502 | this.viewFields.show(); |
|
487 | 503 | } |
|
488 | 504 | }; |
|
489 | 505 | |
|
490 | 506 | var ReviewersPanel = { |
|
491 | 507 | editButton: $('#open_edit_reviewers'), |
|
492 | 508 | closeButton: $('#close_edit_reviewers'), |
|
493 | 509 | addButton: $('#add_reviewer_input'), |
|
494 | 510 | removeButtons: $('.reviewer_member_remove'), |
|
495 | 511 | |
|
496 | 512 | init: function() { |
|
497 | 513 | var that = this; |
|
498 | 514 | this.editButton.on('click', function(e) { that.edit(); }); |
|
499 | 515 | this.closeButton.on('click', function(e) { that.close(); }); |
|
500 | 516 | }, |
|
501 | 517 | |
|
502 | 518 | edit: function(event) { |
|
503 | 519 | this.editButton.hide(); |
|
504 | 520 | this.closeButton.show(); |
|
505 | 521 | this.addButton.show(); |
|
506 | 522 | this.removeButtons.css('visibility', 'visible'); |
|
507 | 523 | }, |
|
508 | 524 | |
|
509 | 525 | close: function(event) { |
|
510 | 526 | this.editButton.show(); |
|
511 | 527 | this.closeButton.hide(); |
|
512 | 528 | this.addButton.hide(); |
|
513 | 529 | this.removeButtons.css('visibility', 'hidden'); |
|
514 | 530 | } |
|
515 | 531 | }; |
|
516 | 532 | |
|
517 | 533 | PRDetails.init(); |
|
518 | 534 | ReviewersPanel.init(); |
|
519 | 535 | |
|
536 | showOutdated = function(self){ | |
|
537 | $('.comment-outdated').show(); | |
|
538 | $('.filediff-outdated').show(); | |
|
539 | $('.showOutdatedComments').hide(); | |
|
540 | $('.hideOutdatedComments').show(); | |
|
541 | ||
|
542 | }; | |
|
543 | ||
|
544 | hideOutdated = function(self){ | |
|
545 | $('.comment-outdated').hide(); | |
|
546 | $('.filediff-outdated').hide(); | |
|
547 | $('.hideOutdatedComments').hide(); | |
|
548 | $('.showOutdatedComments').show(); | |
|
549 | }; | |
|
550 | ||
|
520 | 551 | $('#show-outdated-comments').on('click', function(e){ |
|
521 | 552 | var button = $(this); |
|
522 | 553 | var outdated = $('.comment-outdated'); |
|
554 | ||
|
523 | 555 | if (button.html() === "(Show)") { |
|
524 | 556 | button.html("(Hide)"); |
|
525 | 557 | outdated.show(); |
|
526 | 558 | } else { |
|
527 | 559 | button.html("(Show)"); |
|
528 | 560 | outdated.hide(); |
|
529 | 561 | } |
|
530 | 562 | }); |
|
531 | 563 | |
|
532 | 564 | $('.show-inline-comments').on('change', function(e){ |
|
533 | 565 | var show = 'none'; |
|
534 | 566 | var target = e.currentTarget; |
|
535 | 567 | if(target.checked){ |
|
536 | 568 | show = '' |
|
537 | 569 | } |
|
538 | 570 | var boxid = $(target).attr('id_for'); |
|
539 | 571 | var comments = $('#{0} .inline-comments'.format(boxid)); |
|
540 | 572 | var fn_display = function(idx){ |
|
541 | 573 | $(this).css('display', show); |
|
542 | 574 | }; |
|
543 | 575 | $(comments).each(fn_display); |
|
544 | 576 | var btns = $('#{0} .inline-comments-button'.format(boxid)); |
|
545 | 577 | $(btns).each(fn_display); |
|
546 | 578 | }); |
|
547 | 579 | |
|
548 | 580 | $('#merge_pull_request_form').submit(function() { |
|
549 | 581 | if (!$('#merge_pull_request').attr('disabled')) { |
|
550 | 582 | $('#merge_pull_request').attr('disabled', 'disabled'); |
|
551 | 583 | } |
|
552 | 584 | return true; |
|
553 | 585 | }); |
|
554 | 586 | |
|
555 | 587 | $('#edit_pull_request').on('click', function(e){ |
|
556 | 588 | var title = $('#pr-title-input').val(); |
|
557 | 589 | var description = codeMirrorInstance.getValue(); |
|
558 | 590 | editPullRequest( |
|
559 | 591 | "${c.repo_name}", "${c.pull_request.pull_request_id}", |
|
560 | 592 | title, description); |
|
561 | 593 | }); |
|
562 | 594 | |
|
563 | 595 | $('#update_pull_request').on('click', function(e){ |
|
564 | 596 | updateReviewers(undefined, "${c.repo_name}", "${c.pull_request.pull_request_id}"); |
|
565 | 597 | }); |
|
566 | 598 | |
|
567 | 599 | $('#update_commits').on('click', function(e){ |
|
568 | 600 | var isDisabled = !$(e.currentTarget).attr('disabled'); |
|
569 | 601 | $(e.currentTarget).text(_gettext('Updating...')); |
|
570 | 602 | $(e.currentTarget).attr('disabled', 'disabled'); |
|
571 | 603 | if(isDisabled){ |
|
572 | 604 | updateCommits("${c.repo_name}", "${c.pull_request.pull_request_id}"); |
|
573 | 605 | } |
|
574 | 606 | |
|
575 | 607 | }); |
|
576 | 608 | // fixing issue with caches on firefox |
|
577 | 609 | $('#update_commits').removeAttr("disabled"); |
|
578 | 610 | |
|
579 | 611 | $('#close_pull_request').on('click', function(e){ |
|
580 | 612 | closePullRequest("${c.repo_name}", "${c.pull_request.pull_request_id}"); |
|
581 | 613 | }); |
|
582 | 614 | |
|
583 | 615 | $('.show-inline-comments').on('click', function(e){ |
|
584 | 616 | var boxid = $(this).attr('data-comment-id'); |
|
585 | 617 | var button = $(this); |
|
586 | 618 | |
|
587 | 619 | if(button.hasClass("comments-visible")) { |
|
588 | 620 | $('#{0} .inline-comments'.format(boxid)).each(function(index){ |
|
589 | 621 | $(this).hide(); |
|
590 | 622 | }); |
|
591 | 623 | button.removeClass("comments-visible"); |
|
592 | 624 | } else { |
|
593 | 625 | $('#{0} .inline-comments'.format(boxid)).each(function(index){ |
|
594 | 626 | $(this).show(); |
|
595 | 627 | }); |
|
596 | 628 | button.addClass("comments-visible"); |
|
597 | 629 | } |
|
598 | 630 | }); |
|
599 | 631 | }) |
|
600 | 632 | </script> |
|
601 | 633 | |
|
602 | 634 | </div> |
|
603 | 635 | </div> |
|
604 | 636 | |
|
605 | 637 | </%def> |
General Comments 0
You need to be logged in to leave comments.
Login now